Mailing is a tool for building, testing, and sending emails with React.

  • Build email templates with React components Examples
  • MJML components that work across clients (Outlook!)
  • Preview server with live reload for quick development
  • Dev mode opens emails in your browser instead of sending
  • Test mode for ensuring emails send and have the correct content
  • Plays well with js frameworks like Next.js, Redwood.js, Remix
  • Written in TypeScript, inspired by Action Mailer from Ruby on Rails

  1. Install mailing with yarn or npm:
yarn add mailing-core next react react-dom
yarn add --dev mailing
  1. Run npx mailing to start the preview server and scaffold your emails directory. To force TypeScript mode, run npx mailing --typescript. This will create the following directory for all of your emails:
├── Welcome.tsx
├── components // shared components go in here
│   ├── BaseLayout.tsx
│   ├── Button.tsx
│   ├── Footer.tsx
│   ├── Header.tsx
│   ├── Heading.tsx
│   ├── Link.tsx
│   └── Text.tsx
├── index.ts // this exports sendMail and is where your SMTP config goes
├── previews // use previews to develop and check templates
│   └── Welcome.tsx
└── theme.ts // use this to customize your email theme
  1. Configure your email transport and defaultFrom in emails/index.ts. It defaults to nodemailer's SMTP transport, but you can read about others here.

Example SendGrid transport:

const transport = nodemailer.createTransport({
  host: "",
  port: 587,
  auth: {
    user: "apikey",
    pass: process.env.SEND_GRID_KEY,
  1. Finally, send your first email like so:
import sendMail from "./path/to/emails";
import AccountCreated from "emails/AccountCreated";

await sendMail({
  subject: "My First Email",
  to: "",
  cc: "",
  bcc: ["", ""],
  component: <AccountCreated firstName="Amelita" />,

This will send an email with the subject "My First Email" to the to, cc, and bcc addresses. The component prop is the React component that will be rendered to HTML for the email.

More info on sending email and configuring your transport can be found in the Sending email doc.

Running npx mailing will boot the preview server on localhost:3883 and show you all previews in emails/previews. The previews live reload when files in the emails directory change. Previews are just functions that return one of your emails loaded up with props. You can use them to render your templates in different contexts and related templates can be grouped in the same preview file. You can make your preview function async if you'd like to fetch data inside of it.

For example, here's emails/previews/AccountCreated.tsx:

import AccountCreated from "../AccountCreated";

export function accountCreated() {
  return <AccountCreated name="Amelita" />;

On the left, you'll see a list of all of your emails. On the right, you'll see an individual email preview with a mobile/desktop/HTML toggle and live reload as you edit:

Mailing desktop preview

When your email is nice, send it to yourself or your QA tool of choice for final testing (we like Litmus):

Mailing mobile preview

Disabling email previews in production

If you'd like to disable previews in production, set an environment variable DISABLE_PREVIEWS=1.

When NODE_ENV === "test", calling sendMail pushes messages into a queue for later examination. The mail-core package exports a couple of functions for testing that emails send with the correct content.

function getTestMailQueue(): Promise<Mail[]>

Retrieve the test message queue.

function clearTestMailQueue(): Promise<void>

Clear the test message queue. You probably want to call this before tests that use the queue.


import sendMail from "./path/to/emails";
import { getTestMailQueue, clearTestMailQueue } from "mailing-core";
import IssueNotification from "emails/IssueNotification";

describe("Example API", () => {
  it("sends an email when an issue is ready for review", () => {
    await clearTestEmailQueue();

    // await sendMail({
    //   to: "",
    //   subject: "test",
    //   component: <TextEmail ... />,
    // });

    const emails = await getTestMailQueue();
    expect(emails[0].subject).toMatch("Re: An issue title");
    expect(emails[0].html).toMatch("READY FOR REVIEW");
    expect(emails[0].html).toMatch("ready for QA");

npx mailing init initializes a project then starts the development server

npx mailing preview launches the development server

npx mailing server build builds the next app in .mailing

npx mailing server start starts the next app built in .mailing/.next

npx mailing server builds and starts it

npx mailing export-previews exports template previews as plain html files

npx mailing runs init then preview

Append --help to your CLI command for a full list of supported options. Any of these options can be added to your config file.


Running npx mailing init generates a mailing.config.json file that will be used as default options for the CLI commands. The default options are:

    "typescript": ???, // ("true" if you have a tsconfig.json in your root, otherwise "false")
    "emailsDir": "./emails",
    "outDir": "./previews_html" // (directory for export-previews html output)


To understand how people are using mailing so that we can prioritize our effort, Mailing collects some anonymized usage telemetry. To opt out, set anonymousId to null in mailing.config.json .

Need help getting set up? Join us on Discord.