# Email System Transactional email templates for Next.js 14 via Resend — welcome, verify, password reset, purchase receipt, refund, team invite, affiliate payout, generic notification, and newsletter blast, all sharing a single branded HTML layout. ## What's included **Transactional senders** - `sendWelcomeEmail(to, name)` — account creation confirmation with CTA to browse blocks - `sendEmailVerification(to, name, token)` — verification link with 24-hour expiry note; includes plain URL fallback - `sendPasswordResetEmail(to, name, token)` — reset link with 1-hour expiry note; includes plain URL fallback - `sendPurchaseReceipt(to, opts)` — receipt with line item table, GitHub repo link, and 30-day guarantee notice; accepts `PurchaseReceiptOpts` - `sendRefundEmail(to, opts)` — refund confirmation with amount and PayPal timeline note - `sendTeamInvite(to, opts)` — workspace invitation with role, 7-day expiry, and plain URL fallback - `sendAffiliatePayoutEmail(to, opts)` — payout notification with amount and destination PayPal email - `sendNotificationEmail(to, opts)` — generic template accepting `title`, `body`, optional `ctaText`/`ctaUrl`; use for one-offs **Batch & newsletter** - `sendBatch(emails)` — chunks up to 100 emails per Resend batch call; handles arrays of any size - `sendNewsletter(opts)` — builds per-recipient HTML (individual unsubscribe tokens) and calls `sendBatch` **Utilities** - `generateUnsubToken(email)` — HMAC-SHA256 token derived from email + `NEXTAUTH_SECRET`; deterministic so you can verify it server-side without storing it - `isValidEmail(email)` — regex + length check (≤ 320 chars) - `sendTestEmail(to)` — sends a minimal delivery test; use in dev to confirm Resend is wired **Internals (not exported but worth knowing)** - `layout(opts, body)` — shared HTML shell with logo, card, footer, and unsubscribe link; inline CSS only for email client compatibility - `escHtml(s)` — HTML-escapes all user-controlled strings before injection - `injectUnsub(html, email)` — replaces `{{UNSUB_TOKEN}}` placeholder with the generated token ## Setup ### 1. Install dependencies ```bash npm install resend ``` ### 2. Environment variables ``` RESEND_API_KEY=re_xxxxxxxxxxxxxxxxxxxx FROM_EMAIL=noreply@yourdomain.com NEXT_PUBLIC_APP_URL=https://yourdomain.com NEXTAUTH_SECRET=your NextAuth secret # used to sign unsubscribe tokens ``` ### 3. Add to your project ```ts // Server-only — never import in client components import { sendWelcomeEmail } from '@/blocks/email' ``` You'll also need to handle the unsubscribe route at `GET /unsubscribe?token=...` — verify the token with `generateUnsubToken(email)` and mark the user as unsubscribed in your database. ## Usage examples ```ts // After registration import { sendWelcomeEmail, sendEmailVerification } from '@/blocks/email' import { registerUser } from '@/blocks/auth' const { user, verifyToken } = await registerUser({ name, email, password }) await sendWelcomeEmail(email, name) await sendEmailVerification(email, name, verifyToken) ``` ```ts // After a confirmed PayPal capture import { sendPurchaseReceipt } from '@/blocks/email' await sendPurchaseReceipt(user.email, { name: user.name, blockName: 'Auth System', blockId: 'auth', amount: 19, currency: 'USD', orderId: paypalOrderId, githubRepo: 'marrowstack-auth', githubOwner: 'marrowstack', }) ``` ```ts // Newsletter to all pro users (from admin route) import { getAllUserEmails } from '@/blocks/admin' import { sendNewsletter } from '@/blocks/email' const emails = await getAllUserEmails({ proOnly: true }) await sendNewsletter({ subject: 'New block drop: Billing System', title: 'New block just dropped 🎉', body: 'The Billing & Subscriptions block is now live...', ctaText: 'View Block', ctaUrl: 'https://marrowstack.dev/blocks/billing', recipients: emails.map(e => ({ email: e, name: '' })), }) ``` ## Notes - Every user-supplied string passes through `escHtml` before being interpolated into templates — do not skip this if you add new templates - `sendNewsletter` generates a unique unsubscribe token per recipient, so each email has a different footer link; `sendBatch` just passes HTML through as-is, so if you use it directly you're responsible for injecting tokens - `BRAND`, `ACCENT`, and the hardcoded block count (`17`) in `sendWelcomeEmail` are module-level constants — update them before shipping; they're not driven by env vars - Resend's free tier allows 3,000 emails/month and 100/day — `sendBatch` will silently succeed per chunk but you can hit the daily cap mid-blast; add rate limiting or schedule large newsletters in chunks if you're on the free plan