For React Developers

Best Email Tools for React and Next.js Developers

For a React or Next.js app, write templates as React Email components and send them with Resend: React Email's docs recommend it, the SDK accepts a component directly, and an uploaded template can feed Resend automations. Choose Postmark when product mail must stay strict and boring, Amazon SES when cost per message decides, Loops when app events should start sequences that marketing edits, and Customer.io when journeys branch on product behavior.

Code samples follow each vendor's current documentation page, linked next to the sample. Prices were read from each vendor's pricing page on 7 October 2026.

Our pick for each React situation

One vendor, product mail plus sequences: Resend - the SDK renders your component, and React Email can upload templates to it for automations
Receipts and resets only, delivery first: Postmark - a plain sendEmail call that takes the HTML you render, flat Basic, Pro and Platform steps
Lowest cost per message, already on AWS: Amazon SES - $0.16 per 1,000 emails on Essentials
Marketing edits sequences, the app fires events: Loops - sendEvent from a route handler, free up to 1,000 contacts
Journeys that branch on plan or usage: Customer.io - identify and track calls feeding a visual workflow builder, Essentials from $100
Company already contracts with Twilio: SendGrid - same render-and-send path, Email API and Marketing Campaigns on one account
Template layer: React Email - MIT-licensed, with previews, plain text and internationalization guides; MJML and Maizzle only if you already use them

Three questions a React team answers before picking a vendor

A marketing team picks an email tool by comparing editors. A React team has to settle where the template lives, who renders it and what starts the send, and the answers narrow the field faster than any feature table.

Where does the template live?

A template written as a component sits in your repository, is reviewed in pull requests, takes typed props and previews on your machine. That is the React Email route, and it works with every sender below. The other route keeps the email inside the vendor's editor, as Loops' Next.js guide does, and your code sends only data variables and events. Decide per message type: receipts and resets in code, lifecycle copy in whatever tool marketing edits.

Who renders it?

With Resend the SDK converts the component for you. With Postmark, SendGrid, Mailgun and SES the documented pattern is to call render from React Email yourself and pass the HTML string. That step runs inside your route handler or worker, under your function's time limit. Tutorials written for earlier releases import from @react-email/components and call render without await; the current docs import from react-email and await the result, so check the version you install.

What starts the send?

In a Next.js app a send starts in a route handler, a Server Action, a webhook from a billing or auth provider, or a background worker. Lifecycle tools add another trigger: an event posted to Loops or Customer.io that starts a journey. All of these must run on the server. Loops' guide is explicit that calling its API from the browser exposes your key, and that its API does not support cross-origin requests.

How each tool takes a React Email template, and what it costs to start

Tool Role Best for Template path Entry price
#1 Resend React Email sender with hosted templates and automations One vendor for product mail and sequences Component goes in the react field; no render call Free for 3,000 emails/mo; Pro $20/mo for 50,000
#2 Postmark Strict transactional sender Product mail only, delivery before features await render(...) then the HtmlBody field Basic $15/mo at 10,000 emails
#3 Loops App events start lifecycle sequences Marketing edits the sequence, engineering fires the events None: emails are built in the Loops editor and filled with data variables Free to 1,000 contacts; $99/mo at 10,000 subscribers
#4 Customer.io Behavior-triggered journeys Journeys that branch on plan, usage or role None: journeys and messages are built in its visual workflow builder Essentials from $100/mo for 5k profiles
#5 Amazon SES Lowest cost per message, most plumbing left to you Teams already running queues and IAM on AWS await render(...) then Message.Body.Html.Data $0.16 per 1,000 emails on Essentials
#6 SendGrid Twilio-backed sender with a separate Marketing Campaigns product Accounts that already contract with Twilio await render(...) then the html field Essentials from $19.95/mo
#7 Mailgun Sender with US and EU regions and plan-based log retention Sending from the EU region or keeping longer logs await render(...) then the html field Basic $15/mo at 10,000 emails
#8 Brevo Transactional sending and marketing automation in one account Marketing wants one workspace for campaigns and contacts Render to HTML; React Email lists a Brevo integration page Free for 300 sends a day; Standard from $18/mo
#9 MailerSend Transactional sender with templates and a small free tier Prototypes and small apps that want API plus SMTP Render to HTML; React Email lists a MailerSend integration page Free for 500 emails/mo; Starter $31.50/mo for 50,000
#10 EmailOctopus Subscriber-priced newsletter tool A product newsletter next to the app, nothing triggered Not a send target for app-triggered React Email mail Free for 2,500 subscribers; Pro $9/mo at 10,000

Write the template once, render HTML and plain text from it

React Email's setup is npx create-email@latest, which creates a react-email-starter folder with sample templates. After you install dependencies, npm run dev starts the preview server at localhost:3000 and edits to files in the emails folder show up live. Inside a template you import primitives such as Html, Button, Hr and Text. The render page of the docs shows the conversion: render returns an HTML string, pretty tidies it, and the result carries inline styles and Outlook conditional comments that nobody would write by hand.

Plain text deserves a line of its own in the pipeline. The docs explain that some clients cannot show HTML and some recipients turn it off, and they give you toPlainText(html) to build the alternative from the same template; render also accepts the options pretty, plainText and htmlToTextOptions. Put data-skip-in-text="true" on an element to drop it from the text version only, which suits a decorative banner. Send both versions whenever the sender's API has a field for each.

Attach PreviewProps to the template function so the preview server has realistic data instead of empty props; the internationalization guide does exactly this with name and locale. The inbox snippet beside the subject line has its own Preview component, listed in the docs next to Tailwind, Button and the layout primitives.

For translated mail, React Email documents both next-intl and react-i18next. The next-intl guide keeps one JSON file per locale (messages/en.json, es.json, pt.json), adds a locale prop to the template and to its PreviewProps, builds a translator with createTranslator inside the async template, and has every caller pass the locale. One limit to plan around, stated in that guide: the preview server cannot see the next-intl context of your Next.js app, so use createTranslator in the template and not the app's hooks.

Render and plain text, from the React Email docs

Source: react.email/docs/utilities/render

// email.tsx
import * as React from 'react';
import { Html, Button, Hr, Text } from "react-email";

export function MyTemplate(props) {
  return (
    <Html lang="en">
      <Text>Some title</Text>
      <Hr />
      <Button href="https://example.com">Click me</Button>
    </Html>
  );
}
export default MyTemplate;

// Anywhere on the server
import { MyTemplate } from './email';
import { toPlainText, render } from 'react-email';

const html = await render(<MyTemplate />);
const text = toPlainText(html);

Send from a route handler, or from a Server Action

Resend's Next.js guide puts the template in components/email-template.tsx and the send in app/api/send/route.ts. The sample below returns the SDK's error object as a 500 response and also catches thrown errors, so a failed provider call becomes a visible status rather than a silent success.

Resend in an App Router route handler

Source: resend.com/docs/send-with-nextjs

// app/api/send/route.ts
import { EmailTemplate } from '../../../components/email-template';
import { Resend } from 'resend';

const resend = new Resend(process.env.RESEND_API_KEY);

export async function POST() {
  try {
    const { data, error } = await resend.emails.send({
      from: 'Acme <onboarding@resend.dev>',
      to: ['delivered@resend.dev'],
      subject: 'Hello world',
      react: EmailTemplate({ firstName: 'John' }),
    });

    if (error) {
      return Response.json({ error }, { status: 500 });
    }

    return Response.json(data);
  } catch (error) {
    return Response.json({ error }, { status: 500 });
  }
}

A Server Action can call the same function. For work that should not hold the response, Next.js documents after (imported from next/server): it runs a callback once the response or prerender has finished, works in Route Handlers and Server Functions, and keeps running for the platform's maximum duration, which maxDuration can raise where your host allows it. Per the same page it also runs when the response ended in an error. A welcome email fits that shape. A magic link does not, because the user is waiting on the page for it. The price of after is that nobody sees a failure inside the callback, so write the intended send to your database first.

Keep the provider call behind one function, for example lib/email/send.ts, and never import a vendor SDK from a route or an action directly. That wrapper is where the sender address, tags and an idempotency key belong, and it is the one file a vendor change touches. The Postmark sample below shows why that is cheap: only the client and option names change, never the template.

The same template through Postmark

Source: react.email/docs/integrations/postmark

import { render } from 'react-email';
import postmark from 'postmark';
import { Email } from './email';

const client = new postmark.ServerClient(process.env.POSTMARK_API_KEY);

const emailHtml = await render(<Email url="https://example.com" />);

const options = {
  From: 'you@example.com',
  To: 'user@gmail.com',
  Subject: 'hello world',
  HtmlBody: emailHtml,
};

await client.sendEmail(options);

The React Email pages for SendGrid, Mailgun and Amazon SES have the same three steps: build the client, await render(...), and call the vendor's send with the HTML in that vendor's field. The field is html for SendGrid and Mailgun, HtmlBody for Postmark, and Message.Body.Html.Data for SES. Resend skips the middle step because its SDK takes the component itself.

Start lifecycle sequences from events your app already knows about

Resend automations, Loops and Customer.io share one design from the app's side: your server reports that something happened, and the tool decides which emails follow. The question for a React team is where the emails are authored. In Resend, a template you uploaded from React Email is what the automation sends. In Loops, the sequence is built in the Loops editor. In Customer.io, it is built in the visual workflow builder. Your code only supplies identities, attributes and event names.

For Loops, create a workflow with the Event received trigger, add emails, timers and audience filters, then call sendEvent from the code path that knows the user finished the step. The send-event reference adds two rules that catch people out: properties must be created in Loops before an API call uses them, and contact properties placed at the root of the request body update the contact in the same call. Use event names your team will still recognise in a year; the guide's example is completedOnboarding.

Loops: send an event that starts a workflow

Source: loops.so/docs/guides/nextjs (server-side code only)

import { LoopsClient } from "loops";

const loops = new LoopsClient(process.env.LOOPS_API_KEY);

const resp = await loops.sendEvent({
  email: "test@example.com",
  eventName: "completedOnboarding",
});

if (!resp.success) {
  // The event was not sent
} else {
  // The event was sent OK
}

With Customer.io the unit is a person plus an event. The README for customerio-node creates a TrackClient from a site ID, an API key and a region, then calls identify to create or update the person and track to record what they did. Set the region deliberately: an EU account that omits it is routed from the US to the EU region, and the README warns that this may cause data to be logged in the US. Remember the billing note from the vendor section as well, because every identified profile counts toward the month.

Customer.io: identify a person, then track an event

Source: github.com/customerio/customerio-node

const { TrackClient, RegionUS, RegionEU } = require("customerio-node");
let cio = new TrackClient(siteId, apiKey, { region: RegionUS });

const customerId = 1;

await cio.identify(customerId, { first_name: "Finn" });

return cio.track(customerId, {
  name: "updated",
  data: {
    updated: true,
    plan: "free",
  },
});

Resend's route is the third option: define a custom event, build the automation from its trigger, and send the event with the SDK. Its automation documentation covers the trigger, delay and wait-for-event steps and the per-run history, and the API-first email guide compares the three event APIs by endpoint, retry safety and webhook signing, which this page deliberately does not repeat.

Receive delivery webhooks in a route handler without breaking the signature

Bounces and complaints reach your database through webhooks, and a Next.js route handler is a natural place to receive them. Resend signs its webhooks and puts the signing secret on the webhook details page. Its documentation verifies a request inside a route handler with resend.webhooks.verify and warns that the signature depends on the raw request body: a framework that parses the JSON and stringifies it again will break verification. In an App Router handler that means calling req.text() before anything else reads the body.

One adaptation is needed. The docs sample reads headers like properties of an object, but a Next.js request exposes them through req.headers.get(...), which is what the sample below uses; under strict TypeScript you will also need to handle the null that get can return. The imports and the Resend client are added so the block runs as a file.

Resend: verify a webhook in a route handler

Source: resend.com/docs/webhooks/verify-webhooks-requests

import { NextRequest, NextResponse } from 'next/server';
import { Resend } from 'resend';

const resend = new Resend(process.env.RESEND_API_KEY);

export async function POST(req: NextRequest) {
  try {
    const payload = await req.text();

    // Throws an error if the webhook is invalid
    // Otherwise, returns the parsed payload object
    const result = resend.webhooks.verify({
      payload,
      headers: {
        id: req.headers.get('svix-id'),
        timestamp: req.headers.get('svix-timestamp'),
        signature: req.headers.get('svix-signature'),
      },
      webhookSecret: process.env.RESEND_WEBHOOK_SECRET,
    });

    // Handle the result after validating it
  } catch {
    return new NextResponse('Invalid webhook', { status: 400 });
  }
}

The other vendors differ. Postmark does not sign webhooks and relies on Basic authentication plus its IP ranges, and it wants a 200 once the event is processed or queued, with a non-2xx answer to trigger its retry. Loops signs events with webhook-id, webhook-timestamp and webhook-signature headers, supports one webhook endpoint per account, and delivers at most 10 events a second. Whatever the vendor, acknowledge quickly, store the event, and let a worker do the slow part: a handler that also waits on your database and an outbound call can time out, and the vendor will then retry an event you already half-processed.

Keep templates in their own workspace package

React Email's npm-workspaces page creates a folder called transactional inside packages/*, adds it to the root package's workspaces, installs @react-email/ui as a pinned dev dependency next to pinned react-email, react and react-dom, and adds a dev script that runs email dev. Templates then live in packages/transactional/emails, and the preview server runs on its own at localhost:3000 without booting the Next.js app. There are matching pages for yarn and bun, and the docs link Turborepo example repositories.

The payoff is that the web app and any worker import the same package, so templates and the render step are versioned together and a template change shows up in one pull request. It also fixes the usual drift problem where the Next.js app and a background job each carry their own copy of the welcome email. Export a function per message from the package, such as renderWelcome(props), rather than the raw components; callers then cannot forget to render, and the plain-text version can be produced in the same place.

Serverless time limits, retries and duplicate sends

A serverless function can be stopped the moment it responds. Loops' Next.js guide says that on Vercel each backend function gets its own lambda, and that you must return the promise or the lambda might be terminated before it resolves. React Email's SendGrid sample calls the send without await, which is how a welcome email gets lost in production while working on a laptop. Await every provider call, or put it inside after.

Retries happen at three layers, and each one can duplicate a send. The client library retries on its own: customerio-node retries transient failures (network errors and the status codes 408, 429, 500, 502, 503, 504, 522 and 524) with exponential backoff and jitter, three times by default, honors Retry-After, and does not retry other 4xx answers. The vendor can de-duplicate: Resend remembers an idempotency key for 24 hours and accepts up to 256 characters, and suggests keys shaped like welcome-user/123456789; Loops does the same with an Idempotency-Key header of up to 100 characters on send-event. Your own code is the third layer, and it is the only one that survives a crashed function.

The third layer is an outbox: when the app decides an email should go out, insert a row keyed by message type and user ID, such as welcome/user_8123, then let the request handler or a scheduled route send pending rows and mark each one sent after the vendor returns an ID. Use the same key as the idempotency key where the vendor supports one. For a small app a table polled by a cron-triggered route is enough; a queue starts to pay off when sends must survive a deploy, when volume spikes past a vendor cap such as Resend Free's 100 a day, or when several services need to enqueue mail.

What sending costs at 10,000, 50,000 and 100,000 emails a month

Product mail from a React app is receipts, confirmations and alerts, so the first table prices plain sending. List prices were read from each vendor's pricing page on 7 October 2026. A dash means the vendor's page shows no price step at that volume.

Sender 10,000 emails 50,000 emails 100,000 emails Arithmetic and source
Resend $20 $20 $35 Free (3,000 emails) is too small for 10,000, so Pro; Pro includes 50,000, and its slider reads $35 at 100,000. resend.com/pricing
Postmark (Basic) $15 $55 - Slider stops are 10,000, 50,000, 125,000 and 300,000 emails; Basic reads $115 at 125,000. There is no 100,000 stop. postmarkapp.com/pricing
Mailgun $15 $35 $75 or $90 Basic at 10,000, Foundation at 50,000. At 100,000 the Foundation slider reads $75, while Scale, which includes 100,000, is $90. mailgun.com/pricing
SendGrid (Essentials) - $19.95 $34.95 The Essentials selector spans 50,000 to 100,000 emails, so it has no 10,000 step; Pro is $89.95 at 100,000. Twilio pricing
Amazon SES (Essentials) $1.60 $8.00 $16.00 10 x $0.16, 50 x $0.16 and 100 x $0.16, because the rate is per 1,000 emails. aws.amazon.com/ses/pricing

Sequence tools at 1,000, 10,000 and 50,000 contacts

Tools that run sequences charge by people rather than by message, so the unit changes. Slider readouts dated 7 October 2026.

Tool 1,000 contacts 10,000 contacts 50,000 contacts What the number is
Loops $0 $99 $249 The free plan covers the 1,000 newest contacts with 4,000 sends in any rolling 30 days; the slider reads $99 at 9,998 subscribers and $249 at 49,989. loops.so/pricing
Customer.io (Essentials) $100 $145 $505+ $100 is the smallest step listed, for 5k profiles, so it also covers 1,000 contacts; the selector reads $145 at 10k profiles and $505+ at 50k+. customer.io/pricing
Resend (Marketing) $0 $80 $250 Free at 1,000 contacts with 10,000 automation runs; the Marketing tab slider reads $80 at 10,000 contacts and $250 at 50,000. Resend Marketing pricing

One worked case: 10,000 registered users

Assume a Next.js app with 10,000 registered users that sends 10,000 product emails a month and runs a lifecycle sequence for all 10,000 contacts. Each row is a stack you could actually run.

Stack Product mail Sequences Monthly total
Loops for both Transactional sends included on paid plans Loops at 10,000 subscribers: $99 $99
Customer.io for both Transactional emails listed as included Essentials at 10k profiles: $145 $145
Resend for both Pro, 10,000 of its 50,000 emails: $20 Marketing at 10,000 contacts: $80 $20 + $80 = $100
Resend plus Loops Pro: $20 Loops at 10,000 subscribers: $99 $20 + $99 = $119
Postmark plus Customer.io Basic at 10,000 emails: $15 Essentials at 10k profiles: $145 $15 + $145 = $160
Amazon SES plus Loops 10 x $0.16 per 1,000 emails = $1.60 Loops at 10,000 subscribers: $99 $1.60 + $99 = $100.60

The sender is the small number in every row: the gap between the cheapest and dearest product-mail cell is $20 minus $1.60, or $18.40, while the sequence column runs from $80 to $145. For a React team the real money decision is therefore whether to rent a sequence engine at all, and the table does not price the alternative, which is the engineering time to build a scheduler, exit rules and suppression handling on a plain sender. Resend lists its Transactional and Marketing sliders as separate tabs, so confirm on its pricing page how the two bills combine before you rely on the third row.

Choose A if, choose B if: eight React situations

Resend with React Email is the default answer. These are the conditions that move the pick, each with the number or documented behavior that moves it.

You are pre-launch or under 3,000 emails a month

Pick: Resend Free for product mail, Loops Free for sequences. Resend's Free plan is 3,000 emails a month, capped at 100 a day, on up to 3 domains. Loops' free plan has unlimited contacts but sends only to the 1,000 newest ones, up to 4,000 sends in any rolling 30 days, with a Powered by Loops footer. Together they cost $0 until one of those limits bites, and Resend's docs give you the delivered@resend.dev address to test with before your domain is verified.

Your signups arrive in bursts

Pick: Resend Pro, or Postmark Basic. A launch-day spike of several hundred signups, each getting a confirmation, passes Resend Free's 100-a-day cap before lunch. Pro is $20 a month for 50,000 emails. Postmark's free tier is 100 emails a month with no overages allowed, so a Postmark app starts on Basic at $15 for 10,000 emails, which is the cheaper way to stop thinking about caps if you do not need Resend's automations.

Marketing must edit sequence copy without a deploy

Pick: Loops. The workflow, timers, audience filters and email copy all live in the Loops editor, and your code only sends events. If you want the first draft of those emails to stay in React, the alternative is Resend: upload the React Email template to Resend Templates, which gives non-engineers the visual editor, and build the sequence as an automation.

The next email depends on plan, usage and role together

Pick: Customer.io. Attributes and events from identify and track calls drive a visual workflow builder, which is what its $100 Essentials entry and $145 at 10k profiles pay for. Budget for the profile rule, because deleted profiles still count for the month. If the sequence is a straight line with a few audience filters, Loops at $99 for 10,000 subscribers covers it with less machinery.

Customers ask where their data is processed

Pick: Customer.io or Mailgun, then read the vendor's DPA. Customer.io's pricing page lists US and EU data centers and a published DPA, and Mailgun's says you can send through the US or the EU from a single account. Twilio publishes a Data Protection Addendum for SendGrid but does not state a hosting region on its pricing page. This page reports only what the pricing pages say; get the DPA and sub-processor list from the vendor and have a lawyer review them before you make a commitment to a customer.

You already run on AWS and send past 100,000 emails a month

Pick: Amazon SES. At Essentials' $0.16 per 1,000 emails, 500,000 emails cost 500 x $0.16 = $80.00. Postmark's slider already reads $245 on Basic at 300,000 emails. Resend's Scale tier reads $350 at 500,000 emails. The gap pays for the plumbing you take on: your own queue, your own bounce handling and your own template storage.

Your company already has a Twilio agreement

Pick: SendGrid. The React Email path is the same await render(...) and html field as every other sender, so the template does not care. What changes is procurement: Essentials starts at $19.95 a month, and Marketing Campaigns, with a free plan for up to 2,000 contacts, can sit on the same account. On a greenfield project with no such contract, Resend or Postmark is the shorter road.

You have MJML templates, or a Tailwind-only design system

Pick: keep MJML or Maizzle as the template layer, and any sender behind it. Rewriting working templates is the expense to avoid. MJML compiles to HTML you pass to the same HtmlBody or html field. Maizzle, a Vue and Tailwind CSS framework per its docs, builds HTML files in a separate step. Choose React Email for anything new, because its docs describe the upload into Resend Templates and cover the render, plain-text and preview steps this page uses.

The ten tools in detail, in the order a React team should try them

The order is fixed editorial judgment for a team that writes React Email templates and wants the least glue code: senders with the shortest documented path first, then the lifecycle tools, then the remaining senders. Each card links the pricing page and the documentation pages it was written from.

#1

Resend

React Email sender with hosted templates and automations

Pass a component in the react field and the SDK does the HTML conversion

Free for 3,000 emails/mo; Pro $20/mo for 50,000

Pro reads $35 at 100,000 emails on the slider; Scale is $90 at 100,000 and $160 at 200,000. The Marketing tab is free at 1,000 contacts.

Resend is the one vendor here where React Email's documentation and the vendor's own Next.js guide describe the same short path. React Email's integration page says Resend, built by the same team, is its recommendation, and the emails.send call accepts your component in a react field and converts it to HTML itself. Every other sender in this list that accepts your own HTML needs an explicit await render(...) first. Resend's Next.js guide keeps the template in components/email-template.tsx and the call in app/api/send/route.ts, with pages/api/send.ts as the Pages Router alternative; it expects a verified domain, and its example sends from onboarding@resend.dev to delivered@resend.dev so you can test before DNS is ready.

The second reason it ranks first for React teams is what happens after the template exists. React Email's CLI has a Resend integration (npx react-email@latest resend setup, using an API key scoped to Full Access), and the preview toolbar can upload a template to Resend Templates, where non-engineers edit it in a visual editor. Resend's automation documentation describes steps started by custom events from your app, with delay steps, wait-for-event steps, conditions that branch, emails sent from Templates, and a run record per execution for debugging. Non-transactional messages in an automation are expected to include the unsubscribe URL variable the docs name.

Resend's pricing page has two sliders. On the sending side, the Free plan gives 3,000 emails a month but only 100 a day and 3 domains, Pro costs $20 for 50,000 emails and moves to $35 when the slider reaches 100,000, and Scale costs $90 for 100,000 and $160 at 200,000. On the Marketing side the unit is contacts: $0 at 1,000 with 10,000 automation runs, $40 at 5,000 and $80 at 10,000 (readings of 7 October 2026). The daily cap is the figure to remember before a launch, because 1,000 signups in one afternoon with one welcome email each is ten times the 100 a day that Free allows.

Best for

One vendor for product mail and sequences

Template path

Component goes in the react field; no render call

Entry price

Free for 3,000 emails/mo; Pro $20/mo for 50,000

Sources: pricing page , Send with Next.js, React Email with Resend, Automations, Idempotency keys, Verify webhook requests

#2

Postmark

Strict transactional sender

Render with React Email, then hand the HTML string to sendEmail

Basic $15/mo at 10,000 emails

Slider stops at 10,000 / 50,000 / 125,000 emails: Basic $15, $55, $115; Pro $16.50, $60.50, $126.50; Platform $18, $66, $138. Free is 100 emails a month.

Postmark has no component rendering of its own, so a React team follows the pattern on React Email's Postmark page: await render(...) turns the component into a string, and client.sendEmail takes that string as HtmlBody next to From, To and Subject. It is a few lines more than Resend, and the extra lines buy independence: the template never learns which sender it is going through, so moving to another vendor later changes an import and the option names, not the component.

Postmark's pricing page names its plans by audience: Basic for solo developers and smaller teams, Pro for start-ups and small businesses, Platform for agencies and larger teams. All three begin at 10,000 emails a month, at $15, $16.50 and $18. Past that the page uses a slider with fixed stops rather than a formula, so this page quotes the stops only: 50,000 emails reads $55, $60.50 and $66, 125,000 reads $115, $126.50 and $138, and 300,000 reads $245, $269.50 and $294. Activity data is kept 45 days by default, and a dedicated IP ($50 a month per IP) is offered to accounts sending 300,000 emails a month or more, on Pro or higher.

The part to plan for in a Next.js app is webhook intake. Postmark's webhooks overview states that it does not support HMAC signature verification and recommends HTTP Basic authentication in the webhook URL, an allowlist of Postmark's IP ranges, and validation of the payload shape inside your handler. It asks you to answer 200 once the event is processed or handed to a durable queue, and to return a non-2xx status when processing fails so that Postmark retries. A route handler can check the Basic auth header in a few lines; the IP allowlist has to be enforced somewhere in front of it.

Best for

Product mail only, delivery before features

Template path

await render(...) then the HtmlBody field

Entry price

Basic $15/mo at 10,000 emails

Sources: pricing page , React Email with Postmark, Webhooks overview

#3

Loops

App events start lifecycle sequences

Your route handler posts an event; the workflow in Loops sends the emails

Free to 1,000 contacts; $99/mo at 10,000 subscribers

Free allows 4,000 sends in any rolling 30 days to the 1,000 newest contacts. Paid plans scale by subscribers: $99/mo at 10,000 and $249/mo at 50,000 per the dated slider readout.

Loops does not render React. Its Next.js guide has you create a transactional email in the Loops editor, add data variables such as a login URL, publish it, and call sendTransactionalEmail with a transactionalId and the dataVariables from server-side code. For a React team that means the words and layout of a sequence live outside your repository. That is the point when marketing owns the copy, and a cost when you wanted every email to go through pull-request review.

What the app gets is a small SDK: npm i loops, written in TypeScript, with a Node 18 minimum. Three calls cover most integrations. updateContact creates or updates a contact (the guide calls it the update-or-create shortcut), sendEvent starts any workflow whose trigger is an event received, and sendTransactionalEmail sends one-off messages. The guide recommends always filling in userId, so that a contact's email address can change later, and it warns that the API must only be called from server code: a call from the browser would expose the key, and the API does not accept cross-origin requests, so a signup form posts to your own route handler first. The send-event endpoint also takes an Idempotency-Key header of up to 100 characters; a key reused within 24 hours returns 409 Conflict.

On Loops' pricing page the free tier is shaped by audience rather than by month. Contacts are unlimited, but mail goes to the 1,000 newest of them, up to 4,000 sends inside any rolling 30 days, and each message carries a small Powered by Loops footer. Paying stops the metering: sends are unlimited, transactional mail to people outside your audience is included, team seats are free, and the bill follows subscribers. The slider read $99 a month at 9,998 subscribers and $249 at 49,989 on 7 October 2026. For an app with a growing user table, the 1,000-contact boundary is the practical free-plan limit well before the send count is.

Best for

Marketing edits the sequence, engineering fires the events

Template path

None: emails are built in the Loops editor and filled with data variables

Entry price

Free to 1,000 contacts; $99/mo at 10,000 subscribers

Sources: pricing page , Set up Loops in Next.js, JavaScript SDK, Send event reference

#4

Customer.io

Behavior-triggered journeys

Identify a person, track what they did, and let the journey branch

Essentials from $100/mo for 5k profiles

Essentials reads $145 at 10k profiles, $235 at 20k and $505+ at 50k+, billed monthly. Premium starts at $1,000/mo billed yearly.

The Node library is customerio-node. You create a TrackClient with a site ID, an API key and a region (RegionUS or RegionEU), then call identify with a customer ID and attributes and track with an event name and data, as the README shows. Since version 5 the library is built on standard fetch, so it runs wherever fetch does; the README names Node.js and Bun as the runtimes its CI covers and says other fetch-compatible runtimes are not part of the test matrix. The same README suggests that new integrations use Customer.io's Data Pipelines JavaScript client instead, so read which client the current documentation pushes before you copy the Track API pattern into a fresh app.

The entry plan on Customer.io's pricing page is Essentials, $100 a month billed monthly, covering 5k profiles (people and objects) and 1 million emails a month. The selector then steps to $145 for 10k profiles, $235 for 20k and $505+ for 50k+, and each extra profile is $0.009. Its billing documentation adds a rule that shapes how a React app should call identify: a profile that exists at any point in the month counts toward that month's bill even if you delete it. An app that identifies every signup, including accounts that never activate, therefore pays for its whole signup list rather than its active users. The plan comparison lists transactional emails as included and managed deliverability as not included on Essentials; Premium starts at $1,000 a month billed yearly, and companies that raised under $10M can apply to a Startup Program with 12 months free.

The reason to pay more than Loops is the journey model: events and attributes from your app feed a visual workflow builder in which a person can branch on what they did, and the pricing page lists US and EU data centers and a published DPA. If your sequences are a straight line of four emails, that machinery is unused; if the next email depends on plan, usage and role together, that is the case the price is paying for.

Best for

Journeys that branch on plan, usage or role

Template path

None: journeys and messages are built in its visual workflow builder

Entry price

Essentials from $100/mo for 5k profiles

Sources: pricing page , customerio-node README, How Customer.io bills

#5

Amazon SES

Lowest cost per message, most plumbing left to you

Render with React Email, then call the AWS SDK

$0.16 per 1,000 emails on Essentials

Pro is $0.22 per 1,000 plus $105 per account per region per month; Enterprise is $0.23 per 1,000 plus $500. New accounts start on Essentials from July 21, 2026.

React Email's AWS SES page uses the SES client from @aws-sdk/client-ses: the template becomes a string through await render(...), and ses.sendEmail takes a Source, a Destination with ToAddresses, and a Message whose Subject and Body.Html carry the data with a UTF-8 charset. The region comes from an environment variable (AWS_SES_REGION in the sample). AWS also publishes a newer client under @aws-sdk/client-sesv2, so check which one your tutorial, your IAM policy and your copy-pasted snippets agree on before you mix them.

Amazon's pricing page lists three plans. Essentials charges $0.16 per 1,000 emails across 0 to 10M a month, Pro charges $0.22 per 1,000 plus a fixed $105 per account per region per month, and Enterprise charges $0.23 per 1,000 plus $500. The arithmetic for a React app is short: on Essentials, 10,000 emails are 10 x $0.16 = $1.60 and 100,000 emails are 100 x $0.16 = $16.00. New accounts start on Essentials from July 21, 2026 unless they choose another plan, eligible new AWS customers can receive up to $200 in credits, and the free plan runs for 6 months after account creation.

What the low price leaves to you is the rest of the stack: template storage is your repository, scheduling is your queue, and delivery feedback arrives through AWS services you wire up yourself. A React team that already runs workers and IAM roles on AWS absorbs that without noticing. A team that deploys to a platform with no AWS footprint takes on credentials and regions just to send a welcome email, and the saving at 10,000 emails a month is small next to the setup.

Best for

Teams already running queues and IAM on AWS

Template path

await render(...) then Message.Body.Html.Data

Entry price

$0.16 per 1,000 emails on Essentials

Sources: pricing page , React Email with AWS SES

#6

SendGrid

Twilio-backed sender with a separate Marketing Campaigns product

Render with React Email, then pass html to the mail client

Essentials from $19.95/mo

The selector reads $19.95 at 50,000 emails and $34.95 at 100,000; Pro is $89.95 at 100,000. The free trial is 100 emails a day for 60 days.

React Email's SendGrid page sets the key with sendgrid.setApiKey, builds the HTML with await render(...), puts it in an options object next to from, to and subject, and calls sendgrid.send(options). Look closely at that last line: the docs sample does not await the send. In a long-running server that is harmless; in a serverless function the invocation can end before the request completes, a risk Loops spells out in its own Next.js guide for Vercel. Await the call, or return its promise, in route handlers.

Twilio sells SendGrid as two products with separate pricing pages. For the Email API you get a 60-day trial limited to 100 emails a day, then Essentials, whose selector reads $19.95 at 50,000 emails and $34.95 at 100,000, and Pro from $89.95 with dedicated IPs included. Marketing Campaigns is priced by contacts: a free plan stores 2,000 contacts and sends 6,000 emails a month, Basic starts at $15 and Advanced at $60. Twilio publishes a Data Protection Addendum, but the pricing page does not state a hosting region.

For a greenfield React app nothing in the render-and-send path is better here than in the cheaper entries above, and the Essentials selector starts at 50,000 emails, so a small app has no paid step to land on. The case for SendGrid is organizational: a company that already has a Twilio agreement, or wants the Email API and Marketing Campaigns on one invoice, can keep the React Email templates and change only the sender.

Best for

Accounts that already contract with Twilio

Template path

await render(...) then the html field

Entry price

Essentials from $19.95/mo

Sources: pricing page , React Email with SendGrid, Marketing Campaigns pricing, Twilio Data Protection Addendum

#7

Mailgun

Sender with US and EU regions and plan-based log retention

Render with React Email, then call messages.create on the mailgun.js client

Basic $15/mo at 10,000 emails

Foundation is $35/mo for 50,000 emails and Scale is $90/mo for 100,000. Free is 100 emails a day.

React Email's Mailgun page uses the mailgun.js package together with form-data: the client is created with the username api and your key, the HTML comes from await render(...), and client.messages.create takes your domain (an environment variable in the sample) plus from, a to array, subject and html. As with SendGrid and SES, the template stays a plain component; only this call is vendor-specific.

Three limits on Mailgun's pricing page matter to a React team more than the headline price. The first is volume: 100 emails a day on the free plan, then 10,000 a month from $15 on Basic, 50,000 for $35 on Foundation and 100,000 for $90 on Scale, with Foundation and Scale free for the first month. The second is how long the logs last: one day on Free and Basic, five on Foundation, 30 on Scale. The third is the slider quirk at 100,000 emails, where Foundation reads $75 and undercuts the Scale price that also covers 100,000, so weigh those two rows against the log retention and dedicated IP you need rather than treating Scale as the next step up.

For a React team the two facts that decide it are log retention and region. If a customer reports a missing email several days later, a one-day log may no longer hold the message. The pricing page also says you can send through the US or the EU from a single account. One dedicated IP is included with Foundation at 100k, Growth and Scale; extra IPs are $59 per IP per month.

Best for

Sending from the EU region or keeping longer logs

Template path

await render(...) then the html field

Entry price

Basic $15/mo at 10,000 emails

Sources: pricing page , React Email with Mailgun

#8

Brevo

Transactional sending and marketing automation in one account

Render with React Email and send the HTML; marketers get a separate campaign editor

Free for 300 sends a day; Standard from $18/mo

Starter is $9/mo with no daily limit, Standard $18/mo adds automation and A/B testing, Professional starts at $499/mo.

Brevo suits a React team when the company wants transactional mail, campaigns and contacts in one account and the app only needs a send call. React Email lists a Brevo page among its integrations, so the template pattern is the same render-then-send shape as the other senders, and the campaign editor stays with the marketers who never open your repository.

Two limits define the free tier on Brevo's pricing page: 300 sends a day, and only 2,000 contacts counted toward marketing automation, although up to 100,000 contacts can be stored. The paid ladder starts at Starter, $9 a month with the daily cap removed, then Standard from $18, where automation, A/B testing and advanced reports first appear, and Professional from $499 for 150,000 to 10 million monthly sends and 10 user seats. The volume you pick on the slider sets the tier, so read the price at your own send count instead of treating the entry figures as the bill.

The trade-off is breadth. One account serving product mail and marketing campaigns is convenient, and it also ties two audiences and two kinds of risk to a single vendor relationship. Whichever vendor you pick, keep a small wrapper module in the app so product mail has one entry point; a later split between a product sender and a campaign tool then touches that module, not every route handler.

Best for

Marketing wants one workspace for campaigns and contacts

Template path

Render to HTML; React Email lists a Brevo integration page

Entry price

Free for 300 sends a day; Standard from $18/mo

Sources: pricing page , React Email with Brevo

#9

MailerSend

Transactional sender with templates and a small free tier

Render with React Email and send the HTML through its API or SMTP relay

Free for 500 emails/mo; Starter $31.50/mo for 50,000

Starter is billed yearly at $378. Hobby is $6.30/mo for 5,000 emails; Professional is $99/mo for 50,000 emails plus 150 SMS.

MailerSend is a plain delivery layer for a React app: the template stays a React Email component, you render it, and MailerSend carries the message through its Email API or SMTP relay. React Email lists a MailerSend page among its integrations, so the render-then-send shape matches the other senders.

The ladder on MailerSend's pricing page starts with a Free plan of 500 emails a month, one domain, one user and 100 API requests a day. Hobby then costs $6.30 a month ($75.60 billed yearly) and brings 5,000 emails and five seats. Starter is $31.50 a month ($378 billed yearly) for 50,000 emails, five seats and ten domains. Professional is $99 a month ($1,188 billed yearly), with the same 50,000 emails, unlimited users and domains, and 150 SMS a month. Usage above a plan is billed per 1,000 emails.

The 100 daily API requests on Free make it a place to prove an integration, not to run a launch. The documented feature list is sending, SMTP relay, bulk emailing, verification, analytics and an SMS API, with no sequence builder among them, so lifecycle emails still need Loops, Customer.io or Resend automations running next to it.

Best for

Prototypes and small apps that want API plus SMTP

Template path

Render to HTML; React Email lists a MailerSend integration page

Entry price

Free for 500 emails/mo; Starter $31.50/mo for 50,000

Sources: pricing page , React Email with MailerSend

#10

EmailOctopus

Subscriber-priced newsletter tool

A list-and-campaign product, not a per-user trigger engine

Free for 2,500 subscribers; Pro $9/mo at 10,000

Free also caps sends at 10,000 emails a month and adds EmailOctopus branding. Pro is shown as $9 per month, billed yearly, at the 10,000 setting.

EmailOctopus belongs on this page only because React teams often need a product newsletter next to the app, and the cheap options matter there. Per emailoctopus.com/pricing, Free covers 2,500 subscribers and 10,000 emails a month with EmailOctopus branding on the messages, reports kept for 30 days, one landing page and form, and one user. Pro is shown at $9 a month, billed yearly, with the selector set at 10,000 subscribers, and adds unlimited landing pages, forms and users.

It is priced and built around lists, so it is the wrong place for an email that must follow a product event within seconds. Use it for the changelog digest or the monthly update, and keep password resets, receipts and event-driven sequences in one of the tools above.

Best for

A product newsletter next to the app, nothing triggered

Template path

Not a send target for app-triggered React Email mail

Entry price

Free for 2,500 subscribers; Pro $9/mo at 10,000

Sources: pricing page

The template layer: React Email, MJML or Maizzle

None of these cost money to use, so they are not ranked with the vendors above. They decide how you write the email, and the sender behind them can change.

React Email

MIT license, per its GitHub repository

React Email is the default template layer on this page because every sender above has a documented path for it and its documentation covers the pieces a Next.js team asks about: a render function, a plain-text converter, a preview server, Tailwind and Preview components, internationalization guides and monorepo setups. It is open source under the MIT license, so the cost of the template layer is zero and all the spend is in the sender.

Fits when: You write React and TypeScript already, and want previews, props and one component per email.

Source

MJML

MIT license, per the MJML FAQ

MJML is a markup language for responsive email with a local compiler, an online editor and editor plugins; its site lists ReactJS among the integrations. Choose it when templates already exist in MJML and rewriting them is the cost you want to avoid. Writing new templates is a different case: components, props and previews are what React Email adds on top, and MJML on its own gives you markup to compile, not a component model.

Fits when: You have existing MJML templates, or designers who already hand over MJML.

Source

Maizzle

Self-hosted framework; no plan prices are published

Maizzle's documentation introduces it as a modern framework for building HTML emails with Vue and Tailwind CSS, with CSS inlining, a build pipeline, a live preview server and a Vite plugin. That makes it a natural fit for a Nuxt or Astro team, and a more distant one for a Next.js team, whose components are React. A Next.js team can still use it as a separate build step that outputs HTML files, at the price of two template systems in one repository.

Fits when: Your design system is Tailwind and your team is happy in Vue or plain HTML.

Source

Go deeper on one decision

Questions React developers ask about email tooling

Why not reuse my app components inside an email?

Email clients do not run your stylesheet or your layout engine. React Email's render output in its own documentation shows what the conversion adds: inline style attributes on each element and conditional comments for Outlook around a button. Components written for the browser rely on classes, flexbox and modern CSS that many mail clients ignore. Share design tokens such as colors, font stacks and spacing numbers between the app and the emails, and keep separate email components built from the React Email primitives.

Can Loops or Customer.io send my React Email templates?

Not through the paths their own documentation shows. Loops' Next.js guide builds the email in the Loops editor and fills data variables from code, and Customer.io's Node README covers identify and track calls rather than template rendering. To keep React components as the source of truth, send product mail through Resend, Postmark or another sender that accepts rendered HTML, or upload the template to Resend Templates and run the sequence in Resend automations. If you split lifecycle mail into Loops, accept that its copy lives in Loops.

Should a Server Action wait for the email to be sent?

Only when the user needs to know it worked, for instance a magic-link request. For a welcome email after signup, Next.js offers after(), which runs a callback once the response has finished and, per its documentation, runs even if the response did not complete successfully, within the platform's maximum duration. The cost is that errors inside the callback never reach the user, so log them and record the send in your database for a retry.

How do I stop a retry from sending the same email twice?

Use the vendor's de-duplication where it exists. Resend accepts an idempotency key of up to 256 characters, remembered for 24 hours, and suggests a format such as welcome-user/123456789. Loops accepts an Idempotency-Key header of up to 100 characters on send-event and answers 409 Conflict when it was used in the previous 24 hours. For senders whose docs describe no equivalent, write the intended send to your database first, mark it sent after the call, and key it by email type plus user ID.

Will these SDKs run on the Edge runtime?

None of the pages checked for this guide promise it. Loops' JavaScript SDK page states a minimum of Node 18. The customerio-node README says it runs on standard fetch, tests Node.js and Bun in CI, and treats Deno, Cloudflare Workers and similar runtimes as untested. Resend's Next.js guide uses route handlers and the Pages Router without naming the Edge runtime. Keep email routes on the Node.js runtime unless a vendor's page tells you otherwise.