For React developers

Best Email Tools for React Developers in 2026

For most React teams, start with Resend plus React Email: the free plan includes 3,000 emails per month and the Pro plan is $20 per month for 50,000 emails. This guide shows where Postmark, SendGrid, Mailgun, Amazon SES, Loops, or MailerLite fit better, and how to build a typed template and delivery workflow that survives production.

The React-specific decision

React gives a team a natural way to compose a welcome message, invoice, verification notice, or product update from small components. Email is still a different rendering target. The recipient may open the message in Gmail, Outlook, Apple Mail, or a mobile client that removes unsupported CSS, ignores JavaScript, and rewrites images. A good architecture therefore separates three concerns: a JSX presentation layer, a server-side sending adapter, and an event and preference layer.

The presentation layer owns the markup. React Email components can keep props explicit, so a missing invoice number fails during development instead of producing an empty line in a receipt. The adapter turns that component into HTML and text, calls one provider SDK, and translates provider errors into an application result. The event layer decides whether the message is transactional or marketing, records the provider message id, and listens for delivery, bounce, complaint, and unsubscribe events. Keeping those boundaries visible is more valuable than choosing a vendor based on a long feature list.

Prices below are the published figures in the research records checked on 7 October 2026. Each entry names the plan, billing view, and volume or audience size that the price covers. A free tier is described with its limits. When a vendor prices by a selector or by usage, the volume is part of the sentence. There are no estimates in the tables.

Email tools for a React codebase

ToolBest forStarting price and billingKey limitation
ResendReact Email plus a modern TypeScript APIFree: 3,000 emails/month; Pro: $20/month for 50,000 emailsThe free plan allows 100 emails/day and three domains.
React EmailThe component and preview layer, independent of a senderOpen-source library: free to useIt renders markup; it does not provide delivery, suppression, or webhooks.
PostmarkTransactional mail with clear message streamsFree: 100 emails/month; Basic: $15/month from 10,000 emailsIts core product is transactional; broadcast campaigns need another workflow.
SendGridA broad API and marketing option for established teamsFree trial: 100 emails/day for 60 days; Essentials: $19.95/month for 50,000–100,000 emailsThe entry plan covers a wide volume band, so small senders may pay for unused capacity.
MailgunRouting, logs, validation, and API controlFree: 100 emails/day; Basic: $15/month for 10,000 emailsThe Foundation plan jumps to $35/month for 50,000 emails after its first month.
Amazon SESAWS-native, high-volume sending with usage pricingEssentials: $0.16 per 1,000 emails for 0–10M emails/monthYou assemble more of the template, event, suppression, and monitoring stack yourself.
LoopsProduct-led SaaS lifecycle mail with a small marketing teamEarly Stage: free for up to 4,000 sends in a rolling 30 days; Growing Audience: $99/month at 9,998 subscribersThe free plan sends to the 1,000 newest contacts and includes a footer.
MailerLiteA marketing editor alongside a React productFree: up to 250 subscribers and 2,500 emails/month; Comfort: $12/month starting priceTransactional sending is pointed to MailerSend, and API sending requires a paid plan.

A maintainable stack: JSX, an adapter, and events

Create an EmailTemplateProps type for every message. Props should contain values the message needs, not a database record. For example, a receipt can accept an order number, a currency-formatted total, line items, and a payment URL. Formatting the amount before it reaches JSX keeps locale and currency decisions in application code. It also makes fixtures easy to read in tests.

type WelcomeProps = {
  firstName: string;
  productName: string;
  dashboardUrl: string;
};

export function WelcomeEmail({ firstName, productName, dashboardUrl }: WelcomeProps) {
  return (
    <Html>
      <Head />
      <Preview>Welcome to {productName}</Preview>
      <Body style={body}>
        <Container style={container}>
          <Heading>Welcome, {firstName}</Heading>
          <Text>Your workspace is ready.</Text>
          <Button href={dashboardUrl} style={button}>Open dashboard</Button>
        </Container>
      </Body>
    </Html>
  );
}

The component does not know whether the message is sent by Resend or Postmark. A provider adapter can accept a discriminated union of message names, render the corresponding component, and call the SDK. That gives a migration a narrow seam. It also prevents a marketing editor’s template id from leaking into a security-sensitive password reset path. Use a dedicated server module, keep credentials in the runtime secret store, and make the adapter idempotent when the surrounding job may retry.

import { Resend } from 'resend';
import { render } from '@react-email/render';
import { WelcomeEmail } from './emails/welcome';

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

export async function sendWelcome(to: string, props: WelcomeProps) {
  const html = await render(WelcomeEmail(props));
  const text = await render(WelcomeEmail(props), { plainText: true });
  return resend.emails.send({
    from: 'Acme <hello@example.com>',
    to: [to],
    subject: 'Welcome to Acme',
    html,
    text,
  });
}

Some SDKs accept a React element directly; the explicit render form remains useful when you want the same HTML and text passed to different providers. Await the render result in the version of React Email you install, and pin dependency versions so a renderer change is reviewed like any other visual change. Never call this module from a browser bundle.

Provider reviews for React developers

#1

Resend

React Email plus a modern TypeScript API

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

Resend is the clearest first choice when the application team owns templates and delivery code. Its JavaScript SDK accepts a rendered React Email component, so a .tsx template can stay next to the React application without becoming a string full of escaped markup. The free tier gives 3,000 emails per month, with a 100-email daily limit and three domains. The Pro plan is $20 per month for 50,000 emails. Those are monthly prices, not annualized estimates. A team can start with the free allowance while wiring domain authentication, then move to Pro when the daily ceiling or volume becomes the constraint. The API returns a data-or-error shape that is convenient to wrap in a typed application service. The limitation is the same reason the product feels simple: teams that need a large visual campaign editor, complex audience governance, or a separate marketing team workflow may want a second system.

Read Resend pricing

#2

React Email

The component and preview layer, independent of a sender

Open-source library: free to use

React Email is a template toolkit rather than an email service. That distinction is useful in a React codebase: the team can own a typed component library and choose Resend, Postmark, SendGrid, Mailgun, or Amazon SES for transport. Components such as Html, Head, Body, Container, Section, Row, Column, Text, Heading, Link, Button, Img, and Hr make the intent visible in JSX. The local preview command gives designers and developers a quick rendering loop, while the render package turns a component into HTML and plain text for an API request. The project still needs a sending provider, bounce processing, event storage, and a policy for images and links. Treat React Email as the presentation boundary, then keep provider code behind an interface so a future migration does not require rewriting every template.

Read React Email pricing

#3

Postmark

Transactional mail with clear message streams

Free: 100 emails/month; Basic: $15/month from 10,000 emails

Postmark suits password resets, receipts, invoices, login links, and other messages where a predictable transactional path matters more than a campaign editor. The free plan is 100 emails per month with no overages. Basic starts at $15 per month from 10,000 emails, and the pricing page lists Pro at $16.50 and Platform at $18 at that same starting volume. A React developer can render JSX with React Email and pass HtmlBody and TextBody to the Node client. Message streams provide a useful separation between outbound transactional traffic and other mail. The trade-off is scope: a product newsletter or lifecycle campaign still needs a marketing sender, and the application team must decide how to keep template versioning in Git while operations staff inspect delivery events.

Read Postmark pricing

#4

SendGrid

A broad API and marketing option for established teams

Free trial: 100 emails/day for 60 days; Essentials: $19.95/month for 50,000–100,000 emails

SendGrid makes sense when one vendor must cover transactional API calls, dynamic templates, and a marketing workspace. The Email API free trial permits 100 emails per day for 60 days. Essentials is listed at $19.95 per month in the selector for 50,000 to 100,000 emails, while Pro starts at $89.95 per month for 100,000 to 2,500,000 emails. In a React service, @sendgrid/mail can receive HTML generated by React Email and a plain-text alternative. The broad API surface includes personalizations, categories, substitutions, templates, and event webhooks. The cost is a larger product surface and a plan selector that deserves a careful volume check. Keep your application’s provider adapter small, and do not let SendGrid-specific dynamic-template identifiers leak into every component.

Read SendGrid pricing

#5

Mailgun

Routing, logs, validation, and API control

Free: 100 emails/day; Basic: $15/month for 10,000 emails

Mailgun is a good engineering choice when troubleshooting delivery is part of the job. The free plan includes 100 emails per day. Basic is $15 per month for 10,000 emails; Foundation is $35 per month for 50,000 emails after the first month; Scale is $90 per month for 100,000 emails after the first month. The Node client uses form-data and exposes messages, domains, routes, and event queries. Render a React Email component on your server and send the resulting HTML through messages.create. EU and US sending regions are listed, and the service includes inbound routing and validation features. Plan the event pipeline early: Mailgun’s useful logs only help if bounces, complaints, and delivery events are stored with a stable application identifier.

Read Mailgun pricing

#6

Amazon SES

AWS-native, high-volume sending with usage pricing

Essentials: $0.16 per 1,000 emails for 0–10M emails/month

Amazon SES is attractive when the application already runs in AWS and the team is comfortable composing infrastructure. The Essentials tier lists $0.16 per 1,000 emails for 0 to 10 million emails per month. The pricing page also describes a free plan for six months after account creation, with eligibility conditions, so do not treat it as a permanent allowance. The AWS SDK for JavaScript sends a rendered HTML string and text body; SNS, CloudWatch, and configuration sets cover event routing and metrics. At 250,000 emails, the published pricing example is 250,000 × $0.16 per 1,000 = $40. The low unit price does not remove the work of verifying domains, requesting production access, handling bounces, and building an operator-friendly delivery view.

Read Amazon SES pricing

#7

Loops

Product-led SaaS lifecycle mail with a small marketing team

Early Stage: free for up to 4,000 sends in a rolling 30 days; Growing Audience: $99/month at 9,998 subscribers

Loops is aimed at product events, onboarding, and lifecycle work rather than a pure infrastructure team. Its Early Stage plan is free, with up to 4,000 sends in any rolling 30 days, a 10-email-per-second limit, and a small footer. The paid Growing Audience plan is shown at $99 per month for 9,998 subscribers; the pricing page also shows $48 at 4,788 subscribers and $249 at 49,989 subscribers. Paid plans include transactional sending to users outside the audience without a separate per-send charge. A React developer can keep application events and template variables typed in an adapter while the marketing team edits audience logic. Use it when that collaboration is the requirement; choose a lower-level API when Git-reviewed markup and provider portability matter more.

Read Loops pricing

#8

MailerLite

A marketing editor alongside a React product

Free: up to 250 subscribers and 2,500 emails/month; Comfort: $12/month starting price

MailerLite belongs on the marketing side of a React stack. The free plan covers up to 250 subscribers and 2,500 monthly emails. Comfort starts at $12 per month, while the pricing page shows an annual-billed monthly rate of $10.80 at 500 subscribers; Power starts at $25 monthly, or $22.50 annual-billed at that tier. Comfort allows 5,000 monthly emails at 500 subscribers, while Power is unlimited. The service lists EU data storage and ISO 27001, plus a visual automation editor. It is not a replacement for a transactional API: the pricing page points transactional email to MailerSend, and API or MCP sending is available on paid plans. Keep receipts and password resets in your API provider, and use MailerLite for campaigns that benefit from marketer ownership.

Read MailerLite pricing

Transactional and marketing mail need different contracts

ConcernTransactional messageMarketing messageReact implementation
TriggerPassword reset, receipt, login link, account alertNewsletter, launch, education, promotionUse separate typed message names and queues
ConsentUsually tied to providing the requested serviceRequires a stored subscription and unsubscribe pathPass a preference decision into the adapter
Template ownerProduct engineering with reviewed codeMarketing or content team with an approval workflowKeep Git templates and visual templates in distinct folders
Failure handlingRetry safely, surface a status, and keep an audit eventSuppress unsubscribed or complained addressesUse provider webhooks and an internal event table
Provider fitPostmark, Resend, Mailgun, SES, or SendGrid APILoops, MailerLite, SendGrid Campaigns, or a dedicated ESPOne interface can route to separate credentials

A shared brand component does not mean shared sending policy. A button, header, or footer can live in a small package, while consent, unsubscribe headers, stream selection, and retry rules remain message-specific. This separation keeps a campaign experiment from changing the rendering or reliability contract of an account security email.

Local preview that catches email-specific mistakes

Add a dedicated emails/ directory and a preview script to the repository. The preview should load realistic fixtures: a short first name, a long name, a missing optional value, a long product title, a three-line address, and a line-item list with enough rows to force the footer below the fold. These cases reveal overflow and wrapping problems earlier than a single ideal screenshot.

Use the preview server to inspect desktop and mobile widths, dark-mode behavior where supported, image alt text, link destinations, and the plain-text rendering. Check that every important action remains a normal link, that the message has a useful preheader, and that a user can understand the next step without loading images. Email clients do not run your React event handlers, so a visual button must be an anchor with a complete HTTPS URL.

Fixture checklist

  • Long and short names
  • Empty optional fields
  • Long URLs and translated copy
  • Zero, one, and many line items
  • Plain-text output
  • Missing image fallback

Preview review

  • Gmail and Outlook snapshots
  • Mobile width and zoom
  • Keyboard-friendly links
  • Preheader and subject pairing
  • Unsubscribe placement for campaigns
  • Dark background contrast

Testing in CI without sending real mail

A dependable pipeline has several small tests instead of one vague “email works” check. First, compile the TypeScript project and render every registered fixture. Second, assert that the HTML contains the expected subject data, a valid action URL, an unsubscribe link when the message is marketing, and a text alternative. Third, snapshot normalized HTML after removing generated ids and whitespace that is not meaningful. A snapshot review makes table nesting or a changed footer obvious in a pull request.

Test the provider adapter with a mocked SDK and a contract fixture. The mock should receive the sender, recipients, subject, HTML, text, and message metadata. Assert that a provider error becomes a typed failure and that a retryable failure does not create a duplicate application event. For integration environments, use a provider sandbox or a mailbox capture service rather than sending to personal addresses from CI. Store API keys in the CI secret manager and make the default test command credential-free.

test('welcome has a text alternative and an action link', async () => {
  const html = await render(<WelcomeEmail {...fixture} />);
  const text = await render(<WelcomeEmail {...fixture} />, { plainText: true });

  expect(html).toContain('https://app.example.com/dashboard');
  expect(html).toContain('Welcome, Ada');
  expect(text).toContain('Open dashboard');
});

test('marketing messages expose unsubscribe', async () => {
  const html = await render(<WeeklyUpdate {...fixture} />);
  expect(html).toContain('/email-preferences');
});

Visual checks still have a place. Run them on a small set of representative templates after a dependency upgrade or layout change. Keep pixel tests focused on meaningful regions so a harmless renderer whitespace change does not block every pull request. The local preview remains the fastest place to diagnose a failure; CI is the guard that stops an accidental regression from reaching a queue.

A four-stage implementation plan

  1. 1. Define the message inventory. List every transactional and marketing message, its trigger, owner, data contract, provider, and retention rule. Give each message a stable key such as auth.password_reset.v2. This inventory prevents a template from becoming an unnamed side effect in a route handler.
  2. 2. Build the React Email package. Add shared tokens, a conservative layout, Preview text, and typed props. Create fixtures for edge cases. Keep application-only components out of this package and make external images explicit. Render both HTML and text in a local preview.
  3. 3. Introduce the provider adapter. Start with one provider and one queue. Map the internal message key to a component and subject, then return a provider message id. Add idempotency around the job so a worker retry cannot create a second receipt. Keep the SDK import in one module.
  4. 4. Close the event loop. Verify your sending domain, publish SPF, DKIM, and DMARC records, and subscribe to delivery, bounce, and complaint webhooks. Store event timestamps and provider ids. Add dashboards for queue failures and bounce spikes. Only then increase volume or connect a marketing audience.

Choosing by project shape

Project shapeStarting pointWhy it fitsWatch closely
Next.js product with a small engineering teamReact Email + ResendTyped JSX, local preview, and a $20/month Pro plan for 50,000 emailsDomain limits and the free plan’s 100-email daily cap
Security-critical transactional serviceReact Email + PostmarkMessage streams and plans that start at 10,000 emails/monthCampaign mail needs a separate workflow
Existing AWS platform at high volumeReact Email + Amazon SES$0.16 per 1,000 emails in Essentials at 0–10M volumeProduction access, events, suppression, and operations
Teams needing routing and delivery diagnosticsReact Email + MailgunAPI, SMTP relay, inbound routing, logs, and validationFoundation’s $35 monthly step at 50,000 emails
Product-led SaaS with marketer-owned lifecycle flowsLoops with an API adapterPaid transactional sending is included and audience workflows are centralAudience-based pricing and the free plan’s newest-contact limit
Newsletter-led product with a visual editorMailerLite plus a transactional APISubscriber and campaign tools with a free 2,500-email allowanceAPI sending requires paid service and transactional mail is separate

Migration notes for an existing React app

Begin by extracting existing HTML strings into components without changing the visible copy. This creates a safe baseline. Replace interpolated markup with typed props one message at a time, and add a fixture for every production variant you discover. Keep the old sender behind the same internal interface until the new output has been reviewed. That makes rollback a provider configuration change rather than a rushed template rewrite.

Next, move side effects out of request handlers. A signup route should write an account event and enqueue a welcome notification; a worker should render and send it. If the provider is slow or unavailable, the user-facing request can still succeed while the job retries. Add a deduplication key based on the event id and message key. For an invoice, the invoice id is a natural key; for a password reset, use the reset token id and an expiry.

Finally, migrate domain authentication and event handling before changing copy. Publish the provider’s DNS records, confirm that a test domain can send, and receive a webhook in a non-production endpoint. Record the old and new provider message ids during a staged rollout. Compare bounce and complaint handling, not just whether one test email appears in an inbox. Once the adapter and event table are stable, a provider change becomes a bounded implementation task.

SDK patterns for the five sending APIs

The provider SDK is an implementation detail, but its shape affects how much code leaks into your application. Resend’s client takes a message object with a React or rendered body and returns data and error values. Postmark’s Node client uses a ServerClient and explicit HtmlBody, TextBody, From, To, Subject, and MessageStream fields. SendGrid’s mail client accepts a message object with an HTML body and personalization arrays. Mailgun’s client calls messages.create with a domain and a form-like payload. Amazon SES uses the AWS SDK command pattern, where the application supplies a Destination, Content, and configuration set. These differences are exactly why a small adapter is worthwhile.

Define one internal result rather than returning the vendor response throughout the codebase. A useful result contains providerMessageId, accepted, and a normalized error category such as invalid_recipient, rate_limited, auth_failed, or temporary_provider_failure. Preserve the original response in a restricted diagnostic field when support needs it, but keep business logic on the normalized category. This also makes a test double simple: it can return a deterministic message id without importing a vendor package.

Do not let SDK defaults choose a sender or stream accidentally. Make the From address, Reply-To address, message stream, configuration set, and tags explicit in the adapter. Tags are useful for correlating a notification with an internal event, but never put secrets or full personal data in them. A stable template key and event id are enough to explain a send later.

Adapter concernReasonExample decision
HTML and textSome clients block markup or users prefer plain textRender both from the same props fixture
Sender identityDifferent domains may have different reputation and policyMap message family to a verified subdomain
Retry categoryA 4xx recipient error should not retry foreverRetry rate limits and network errors with backoff
IdempotencyWorkers can repeat a job after a timeoutUse event id plus message key as a unique key
Provider metadataDelivery events need a join to the application recordStore provider id and internal notification id

Deliverability work that belongs in the repository

A React template can be beautiful and still fail at the mailbox. Treat domain setup as code-adjacent operational work. Record the sending domains and subdomains in a checked-in runbook, document which provider owns each DKIM selector, and keep the return-path or bounce domain visible to the team that manages DNS. Publish SPF and DMARC deliberately; a broad SPF record that accumulates old vendors is harder to reason about than a small record per sending path.

Build a webhook endpoint before production traffic. Authenticate the provider signature, reject replays where the provider supports timestamps, and make event handling idempotent. A delivery event can mark a notification delivered; a bounce can mark the address suppressed for the appropriate category; a complaint can stop marketing sends immediately. Do not delete the original event when a later retry succeeds. The sequence is useful evidence when a customer asks why a message was delayed.

Use a separate subdomain for product mail and campaigns when the provider and policy allow it. A marketing unsubscribe should not suppress a required security alert, while a hard bounce should inform every category. Make that distinction explicit in a preference table with fields for category, status, source, and changed time. The sender checks the table immediately before a marketing enqueue and the worker checks it again before transmission.

Content affects reputation too. Keep subjects descriptive, make the visible link destination match the label, include a text alternative, and give every image useful alt text. Do not hide a critical action in an image. A React component can enforce these rules with required props and a lint rule that disallows an Image without alt text. Small constraints turn a checklist into a repeatable engineering practice.

Cost scenarios without false precision

A useful comparison keeps volume and billing view beside the price. At 50,000 transactional emails per month, Resend Pro is $20 monthly, Postmark’s Basic slider is $55 monthly, and SendGrid Essentials is shown in a 50,000–100,000 band at $19.95 monthly. Mailgun’s Foundation plan covers 50,000 at $35 monthly after the first month. Those figures describe different products and limits, so the cheapest row is not automatically the best operational choice.

At 100,000 emails, Resend’s transactional selector shows Pro at $35 monthly, Mailgun’s Scale plan lists $90 monthly, and Amazon SES Essentials calculates to 100,000 × $0.16 per 1,000 = $16 in usage. Postmark’s published starting tiers and SendGrid’s selector bands should be read at the exact volume you need rather than extrapolated. If you need a dedicated IP, retention, validation, or a marketing audience, add those explicit line items to the worksheet.

Contact-based marketing pricing is a different axis. Loops shows $99 per month at 9,998 subscribers and $249 at 49,989 subscribers, while MailerLite lists $49 monthly for Comfort at 5,000 subscribers and $89 at 10,000. Resend’s Marketing tab shows $40 monthly at 5,000 contacts and $80 at 10,000. The send count can be unlimited or separately constrained, so a monthly contact count cannot be compared directly with an API’s email volume.

Write the volume model in the decision record: messages per active user, retry allowance, password-reset spikes, campaign sends, and expected inactive addresses. Then choose a plan that covers the modeled peak. A precise arithmetic note is more useful than a rounded “cheap” label, especially when a provider’s free plan has a daily or newest-contact restriction.

Accessibility and internationalisation in JSX

Email content is part of the product interface. Use a logical heading order, descriptive link labels, sufficient contrast, and text that still makes sense when images are disabled. The Preview component should carry a concise summary rather than repeating the first heading. A button component should receive a full URL and visible label; avoid “click here” when the link can say “View invoice”. Keep the footer readable at mobile width and include a plain-text address where policy requires it.

Internationalisation deserves a fixture per supported locale. Dates, decimal separators, currency symbols, plural forms, and text expansion can all change the layout. Format values before rendering, but pass locale-aware strings into the component so the same fixture tests what the recipient sees. A long German compound noun or a translated call to action should not make a table cell overflow. Avoid putting user-controlled strings into raw HTML and escape URLs before handing them to an anchor.

A shared token file can define colors, type sizes, and spacing without sharing the browser application’s CSS runtime. Keep email styles close to the component or compile them into the conservative inline form expected by clients. When the brand changes, update the tokens and review snapshots for the welcome, receipt, reset, and campaign templates. This makes visual consistency deliberate instead of dependent on a browser cascade that email clients may discard.

Finally, consider the recipient’s failure mode. If an image host is blocked, the alt text should explain the image’s purpose. If a link scanner opens a URL, the endpoint should not mutate state on a GET request. If a user forwards the message, the text should still identify the product and the action. React components make these decisions visible in code, which is a strong reason to keep templates under review.

Release checklist for a React email change

Before merging a template change, have the pull request show the rendered fixture, the subject and preheader, the provider message key, and the expected audience category. Review the HTML at a narrow mobile width and in a client that uses a different CSS engine. Check every link with a test URL, confirm that the text alternative includes the same action, and verify that a marketing template still carries its preference link. A code review should be able to answer what changed for a recipient without opening the application.

After merging, deploy the template and the sender together when the data contract changed. If the provider hosts a copy of the template, record its version or identifier in the repository and in the notification event. A queue worker may run an older build while a web process runs a newer one, so make contract additions backward-compatible until the old worker fleet drains. For a breaking change, publish a new message key and keep the old renderer available for retries.

Watch the first production batch. Look for render failures, provider rejection counts, delivery latency, bounce categories, complaint events, and the ratio of HTML opens to text clicks where the provider reports them. A sudden change in one metric does not prove the template is bad, but it gives the team a place to investigate. Keep a small rollback switch that routes the message key to the previous renderer without disabling unrelated notifications.

Document the decision after the batch: which fixtures were reviewed, which provider plan and volume were used, which DNS records were changed, and which events were observed. This short record saves time during the next migration and keeps pricing and operational assumptions visible. React’s advantage is not that email becomes a web page; it is that the component, contract, test, and review can live in one familiar engineering workflow.

Build the smallest reliable email layer

Start with one typed React Email component, one server-side provider adapter, a local fixture preview, and a webhook endpoint. Resend is the practical first pick for that workflow; Postmark, SendGrid, Mailgun, Amazon SES, Loops, and MailerLite each become stronger when their particular operating model matches your team.

Compare developer email APIs

React email FAQ

Should a React team render emails on the server?

Yes. Render the component in a server or worker process where secrets and provider credentials are available. A browser should never receive an API key. The render step can produce HTML and plain text, while the React application passes only typed data such as a user name, invoice URL, or reset token. Keep the render call close to the provider adapter so a client component cannot accidentally import it. In a queue worker, load the template version selected by the notification event, set a bounded timeout for the provider call, and classify the result before acknowledging the job. If rendering fails because a required prop is missing, send the job to a review queue rather than retrying it forever. This arrangement also makes local development safer: fixtures can render against a fake provider while production credentials stay in the server runtime.

Can I share web components with email components?

Share design tokens and data contracts, not browser-only components. Email clients do not execute your application bundle, and support for modern CSS varies. A small email component library can reuse brand colors and spacing values while emitting conservative table-based markup. Keep the boundary explicit in the package layout: browser components may depend on client hooks, while email components should be pure functions of props. This lets a designer reuse a color token without accidentally importing a browser-only dependency into the server render bundle.

Which provider is cheapest at high volume?

Amazon SES has the clearest usage price in the records: $0.16 per 1,000 emails in Essentials for 0–10M emails/month. That is transport pricing, so include engineering time, event storage, monitoring, and support in your decision. A provider with a higher unit price can still be the better choice when a small team needs managed event handling, searchable logs, or an editor for non-engineers.

Do transactional and marketing messages belong in one account?

They can share an account, but keep separate streams, domains or subdomains, API keys, unsubscribe rules, and dashboards. A password reset should not depend on a campaign list, and a newsletter should not share the same suppression policy as a security alert.

How do I test responsive email changes in CI?

Render every fixture to HTML and plain text, assert required links and text, and compare a normalized snapshot. Add a small set of screenshot checks in a real email rendering service when a change touches layout. Run the local preview for human review, but keep the automated test deterministic.

Is React Email required?

No. MJML, Maizzle, hand-written HTML, and provider-hosted templates are valid choices. React Email is compelling when the team already thinks in components and wants a local JSX preview. The sender only needs the resulting HTML and text. Choose the renderer your team can test and maintain.

Where should unsubscribe logic live?

Marketing consent and unsubscribe state should be enforced by the marketing system or a shared preference service. Transactional messages that are necessary to provide the product should use a separate category and an explicit policy. Put the decision in the sending service, not in a presentational component.

What should I log from an API send?

Store a provider message identifier, internal notification identifier, template version, recipient hash or internal user id, provider, request outcome, and event timestamps. Avoid logging tokens or the full personal payload. This gives support enough context to trace a message without turning logs into a copy of your user database. Retain only what your support and compliance processes require.