3 min read

Behind the Drop: Collxn's Daily Email Stack

Every morning, Collxn surfaces one record from your vinyl collection and sends it to your inbox. The feature’s called Daily Drop, and under the hood, it's powered by a stack that's simple to read, quick to tweak, and safe to run in preview environments.

Here’s how it all fits together.

System Architecture

The system breaks down into three clean layers:

  1. email-service.ts handles logic and orchestration: fetching data, rendering, sending, logging.
  2. copy.ts provides all the written content: subject lines, CTA text, alt tags, and signature.
  3. DailyDropEmail.tsx defines the visual layout: a React component rendered to HTML via @react-email/render.

This separation keeps things modular: logic, content, and presentation each live in their own file, making everything easier to test and tweak in isolation.

Deps

A quick list of relevant modules and services I'm using:

Email service: the conductor

src/lib/email/email-service.ts

This is the entry point. It does the heavy lifting: fetches the drop, guards against bad sends, renders content, and fires the email.

Golden path:

  1. A job (like a cron or button click) calls sendDailyDropEmail(userId)
  2. Fetches that user’s current daily drop
  3. Skips if already sent, missing data, or invalid email
  4. Renders both HTML and plain text using shared content
  5. Sends via Resend with a stable idempotency key
  6. Marks the drop as "email sent" in the database
// Simplified src/lib/email/email-service.ts
import { Resend } from "resend";
import { render } from "@react-email/render";
import { DailyDropEmail } from "./templates/DailyDropEmail";
import { dailyDropCopy } from "./copy";

export async function sendDailyDropEmail(userId: string) {
  const drop = await getCurrentDailyDrop(userId);
  if (!drop || drop.emailSentAt || !drop.release.coverImage) return;

  const html = render(DailyDropEmail({ /* props */ }));
  const text = `Hi ${name},\n\n${dailyDropCopy.plainText.subtitle}\n\n${link}`;
  const idempotencyKey = `daily-drop-${userId}-${featuredAt.toISOString().split("T")[0]}`;


  await resend.emails.send({
    to: user.email,
    subject: dailyDropCopy.subject,
    html,
    text,
    headers: { "X-Entity-Ref-ID": idempotencyKey },
  });

  await db.dailyDrop.update({ ... });
}

What to note:

  • Email content is generated on the fly; strings are pulled from content object
  • X-Entity-Ref-ID enables idempotent retries
  • Database gets updated once we send to prevent duplicates

Next, let’s look at where all that content comes from.

Copy object: the content

src/lib/email/copy.ts

This is the single source of truth for all written content in the Daily Drop email. That includes subject lines, preview text, CTAs, and even plain text variations.

// Simplified src/lib/email/copy.ts
export const dailyDropCopy = {
  preview: (date: string) => `Your Daily Drop for ${date} is ready — rediscover something good`,
  subject: "🎷 Your Daily Drop is here",
  subtitle: "Today's drop is here. Ready to rediscover something good?",
  ctaButton: "View Your Drop",
  signature: {
    html: "Enjoy the journey,<br />– Collxn",
    text: "Enjoy the journey,\n– Collxn",
  },
  albumArt: { alt: "Album art for today's daily drop." },
  plainText: {
    subtitle: "Your Daily Drop from Collxn is here. Ready to rediscover something good?",
    viewDrop: "View Your Drop",
  },
} as const;

What to note:

  • HTML and plain text copy share a common base
  • Only small word tweaks between formats
  • This structure should make localization or A/B testing easier later
  • You can write, edit, and preview copy without touching JSX or logic

Next, let’s see how that copy gets rendered visually.

Daily Drop component: the layout

src/lib/email/templates/DailyDropEmail.tsx

This is the visual structure of the email. It’s built entirely with @react-email/components. These are safe, inline-styled primitives that work across email clients.

Golden path:

  1. Accepts props like firstName, username, coverImageUrl, and date
  2. Pulls static copy from copy.ts
  3. Outputs a clean, centered layout with album art and CTA
// Simplified src/lib/email/templates/DailyDropEmail.tsx
import {
  Html, Head, Body, Container, Section,
  Text, Img, Button, Link, Preview
} from "@react-email/components";

export const DailyDropEmail = ({ firstName, coverImageUrl, date, username }) => (
  <Html>
    <Head />
    <Preview>{dailyDropCopy.preview(date)}</Preview>
    <Body>
      <Container>
        <Section>
          <Text>Hi {firstName},</Text>
          <Text>{dailyDropCopy.subtitle}</Text>
          <Link href={dropUrl}>
            <Img src={coverImageUrl} alt={dailyDropCopy.albumArt.alt} />
          </Link>
          <Button
            href={dropUrl}
            style={{
              backgroundColor: "#1f2937",
              borderRadius: "6px",
              color: "#ffffff",
              fontSize: "16px",
              fontWeight: "600",
              textDecoration: "none",
              textAlign: "center",
              display: "block",
              padding: "12px 32px",
              margin: "0 auto",
              maxWidth: "200px",
            }}
          >
            {dailyDropCopy.ctaButton}
          </Button>
          <Text dangerouslySetInnerHTML={{ __html: dailyDropCopy.signature.html }} />
        </Section>
      </Container>
    </Body>
  </Html>
);

What to note:

  • We're composing email but it looks like React!
  • CSS is inline and email-client safe
  • All copy comes from copy.ts, not hardcoded

This layout is simple: image, text, button. Clear and skimmable. But the sky is the limit on how complicated you want to get.

Takeaways

As always, separating concerns makes the code easier to manage.

And every morning, this code brings a little joy into someone’s inbox.

Want to see it in action? Sign up for Collxn and get your own Daily Drop.