- Next.js Email Architecture: Transactional & Newsletters
Next.js Email Architecture: Transactional & Newsletters
How to separate transactional, notification, and marketing email flows in Next.js using React Email and the right…

In-depth Next.js guides covering App Router, RSC, ISR, and deployment. Get code examples, optimization checklists, and prompts to accelerate development.
Every Next.js project eventually needs to send email. And almost every project I've seen starts the same way — someone adds a provider, drops a fetch call into a server action, ships it, and moves on. That works fine for the first use case. By the third, things get messy. Order confirmations are sharing infrastructure with weekly newsletters. System alerts are competing with marketing sends for the same daily quota. One blunt template layer is trying to serve everything from password resets to promotional campaigns.
The underlying problem is that "email" isn't one thing. It's three different problems that happen to share a transport protocol, and treating them the same produces real consequences: deliverability issues when marketing sends degrade your transactional reputation, timeouts when newsletters block serverless functions, and fragile code when you're handing one-size-fits-all HTML templates to non-developer teammates who need to update copy.
This guide maps the architecture. Not how to implement any specific part — the spokes in this series handle that — but how the pieces fit together and which decisions actually matter before you write code.
The industry splits email into three categories for good reason. Each has different latency requirements, different volume patterns, a different template contract, and different compliance rules.
Transactional email is triggered by a user action and expected immediately: password reset, order confirmation, invoice, 2FA code. The user is waiting. If the email takes thirty seconds, trust breaks. This is the tier where deliverability is paramount, volume is spiky and user-correlated, and the content is structured data (order number, reset link, customer name) rather than rich layout. Missing one of these emails is a support ticket.
System notifications are triggered by internal events: a background job fails, a webhook fires, a threshold gets crossed, an import completes. The recipients are usually internal — ops team, developers, or per-customer operational alerts. Latency can be minutes. Retries are fine. These emails rarely need brand design; a clear subject line and a plain-text body with the relevant data are enough. The volume is unpredictable and can spike badly if something misbehaves — a misconfigured webhook loop can generate thousands of emails before anyone notices.
Newsletter and marketing email is the bulk send: product updates, content digests, promotional campaigns. Volume is high and scheduled. Latency doesn't matter. What matters is inbox placement, unsubscribe compliance, engagement rates, and the ability for non-developers to edit copy without touching code. These sends share infrastructure with transactional email only at the risk of dragging each other down.
The architectural consequence of this split is that each type wants different things from your email system. Transactional wants low latency and a simple, data-driven template. Notifications want reliability, retries, and minimal template overhead. Marketing wants list management, compliance tooling, and a visual editing layer that doesn't require a deployment.
Whatever providers you end up using, React Email has become the standard template layer in the Next.js ecosystem, and it's worth understanding why before you choose your delivery approach.
The core problem it solves is that email HTML is not web HTML. Email clients — Gmail, Outlook, Apple Mail, Yahoo — have wildly inconsistent CSS support. Outlook uses Word as its rendering engine. Gmail strips certain style tags. display: flex doesn't work in Outlook. <svg> doesn't work in Gmail. Writing HTML that looks consistent across clients means falling back to table-based layouts and extensively inlined styles, which produces markup that's painful to write and impossible to maintain.
React Email gives you a component abstraction over this. You write JSX using their component library (<Container>, <Section>, <Button>, <Text>, and so on), and the output is email-safe HTML. Components are tested across clients. You get TypeScript-typed props, component reuse across templates, and a local dev server where you can preview templates in the browser before sending.
The 2025 tooling additions made it more practical for production use: a spam score checker (powered by SpamAssassin), a compatibility checker that flags CSS and HTML that specific clients don't support (powered by Can I Email), and responsive preview across device sizes. React Email 5.0, released November 2025, added Tailwind 4 support with email-safe CSS compatibility checks and deep integration with Resend's template system.
The key architectural point is that React Email is the view layer only. It doesn't replace your email provider — it renders to an HTML string that you then pass to whatever delivery API you're using. For most providers (Brevo, Postmark, SendGrid, AWS SES), this means calling render() from @react-email/render to get the HTML string, then sending that string through the provider's API. Resend goes one step further and accepts a React component directly in the react: field, handling the render internally.
The spokes in this series show what this looks like in practice. The MCP email guide demonstrates building a React Email component and delivering through Brevo, which covers the render-to-HTML-and-send pattern that applies to any provider. The Brevo transactional templates guide shows the complementary pattern: designing templates in Brevo's visual editor and injecting structured data at send time, which is the right approach when non-developers need to own the template.
The provider decision comes down to which combination of email types you're handling and what you're optimizing for. The four providers worth knowing in the Next.js ecosystem each have a distinct character.
Resend is the developer-first choice. It's built by the same team as React Email, which means the integration is native — you pass a React component directly, no manual render step. The free tier runs about 3,000 emails per month, paid starts at $20/month for 50k sends. It's best positioned for transactional and light notifications. Its marketing and broadcast features exist but are less mature than providers that have been in the marketing space for years. If you're starting a new project and want the cleanest developer experience, this is the default pick.
Brevo is the all-in-one option. One platform covers transactional sends, marketing campaigns, SMS, WhatsApp, and contact list management. The free tier is generous at 300 emails per day (roughly 9,000/month). It's the right choice when you want fewer vendors and you need real marketing automation — list segmentation, campaign scheduling, subscriber management — alongside your transactional sends. The tradeoff is that transactional and marketing sends share the same quota pool, so a large campaign can eat into your transactional headroom. React Email doesn't have a native Brevo integration, but the pattern is straightforward: render to HTML, pass the string to Brevo's API.
Postmark is the deliverability-first choice. It's built specifically for transactional email and has a long reputation for getting messages into inboxes. The Message Streams concept lets you separate transactional and marketing sends into different streams with independent reputation management, which is the right architecture for anything beyond a hobby project. The free tier is small (100 emails per month), and per-email cost is higher than alternatives — you're paying for a premium deliverability infrastructure and the team behind it. If a missed order confirmation or password reset is a serious business event, Postmark is the safe choice.
SendGrid is the enterprise option. It's been around the longest, handles very high volumes, and has the most feature-complete marketing campaigns product alongside its Email API. The tradeoff is that the developer experience feels dated compared to Resend, the pricing gets complex with separate marketing and transactional products, and the free tier is time-limited (100 emails per day for 60 days, then paid). It makes sense when you're operating at scale and need features like dedicated IPs, inbound email parsing, and email validation APIs.
The practical starting point for most Payload CMS + Next.js projects: if you need transactional email only, start with Resend for the DX or Postmark if deliverability is non-negotiable. If you also need newsletter functionality and want one system, Brevo covers both.
The question of whether to send email synchronously from a server action or route handler versus queuing it through a background job matters more than most developers realize — especially on serverless deployments.
Direct sends are fine for low-volume, truly synchronous email where the user is waiting for the send to complete. A 2FA code is a reasonable example: the user is staring at a screen waiting for a six-digit number, so a brief wait for the API call is acceptable. For everything else, you should be queuing.
The serverless problem is specific to Next.js on platforms like Vercel. Cold starts on serverless functions can add several seconds to a first invocation after inactivity. Combined with rendering a React Email template and making an outbound API call to an email provider, you're looking at real latency on user-facing requests and a genuine risk of function timeouts on large sends. Queue-based sends solve this by decoupling the send from the user request: the server action enqueues a job (fast), and a worker processes the job outside the user request path.
For Payload CMS projects, the natural approach is using a Payload jobs collection as the queue: a collection with fields for type, status, payload, attempt count, and scheduled time. A server action or hook creates a job document after the triggering event, and a worker (a separate process or a cron-style route handler) queries pending jobs and processes them. This keeps everything inside your existing Payload infrastructure without adding Redis or an external queue service. For higher-volume or more complex retry semantics, BullMQ with Redis is the production-grade option — it provides native support for delayed jobs, rate limiting, deduplication, and dead-letter queues.
The practical rule is: transactional email that has a clear synchronous user dependency can go direct, but monitor carefully. System notifications and newsletter sends should always be queued.
The Payload email notifications guide covers exactly this pattern: why afterChange hooks don't execute reliably in server action contexts, and how to move the email logic into the server action itself for predictable execution. That's the direct-send pattern done correctly for Payload. The Payload Jobs Queue series covers the full queue implementation if you need background processing.
None of the implementation details matter if your emails land in spam. And since February 2024, Google and Microsoft have enforced requirements that make deliverability configuration mandatory, not optional.
The three DNS records that must exist for every sending domain: SPF (which IP addresses are allowed to send on behalf of your domain), DKIM (a cryptographic signature that proves the message wasn't tampered with), and DMARC (a policy that tells receiving servers what to do when SPF and DKIM checks fail). All major email providers will walk you through setting these up when you add a sending domain — the process is different per provider, but the underlying records are the same.
For anyone sending more than 5,000 emails per day to Gmail addresses, Google now requires SPF, DKIM, and DMARC alignment, a spam complaint rate below 0.3%, and one-click unsubscribe support for marketing sends. Microsoft announced similar enforcement for Outlook in April 2025. Starting November 2025, Gmail began issuing specific error codes (4.7.x for temporary deferral, 5.7.x for permanent rejection) for non-compliant senders rather than just filtering to spam.
The practical implication for newsletter sends is that you need the unsubscribe infrastructure in place before you start sending at volume. For Brevo, this means using their list management (the Contacts API pattern in the Brevo-Sanity integration guide), which handles unsubscribes automatically. For any provider, you need to process bounce webhooks and suppress bad addresses — sending repeatedly to hard-bounced addresses destroys your sender reputation.
If you're using React Email, the spam score tool added in version 4.0 is worth running against your templates before launch. It surfaces content patterns that trigger spam filters before they cost you inbox placement.
The mental model I use for email in a Next.js + Payload stack:
React Email is the shared template layer that all three email types flow through. Each type connects to a different delivery path. Transactional sends go direct from server actions or route handlers when latency is critical, and through the Payload jobs queue when they don't need to be synchronous. System notifications always go through the queue — they're too unpredictable in volume and too tolerant of delay to justify blocking user requests. Newsletter sends go through Brevo's marketing infrastructure, where list management, unsubscribes, and campaign analytics live.
Brevo plays two different roles depending on the use case. For transactional and notification sends, it's a delivery API — you send structured data and it handles SMTP. For newsletter sends, it's the system of record for subscribers and campaign sends. The Brevo transactional templates guide covers the delivery API pattern. The Brevo-Sanity integration guide covers the subscriber and list management pattern.
For the full implementation of each pattern, the spokes in this series each focus on one use case:
The question to ask before starting any implementation: which email type am I building for, and does the sending path match? Getting that right at the start saves a significant amount of rework later.
Let me know in the comments if you have questions about a specific pattern, and subscribe for more practical development guides.
Thanks, Matija