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:
email-service.tshandles logic and orchestration: fetching data, rendering, sending, logging.copy.tsprovides all the written content: subject lines, CTA text, alt tags, and signature.DailyDropEmail.tsxdefines 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:
resend: transactional email delivery@react-email/render: converts React email components into HTML@react-email/components: building block primitives for email-safe React
Email service: the conductor
src/lib/email/email-service.tsThis is the entry point. It does the heavy lifting: fetches the drop, guards against bad sends, renders content, and fires the email.
Golden path:
- A job (like a cron or button click) calls
sendDailyDropEmail(userId) - Fetches that user’s current daily drop
- Skips if already sent, missing data, or invalid email
- Renders both HTML and plain text using shared content
- Sends via Resend with a stable idempotency key
- 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-IDenables 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.tsThis 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.tsxThis 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:
- Accepts props like
firstName,username,coverImageUrl, anddate - Pulls static copy from
copy.ts - 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.