Streamline email creation with React Email
Tags: react, webdev
Published: 2023-06-13 16:23:54

**Written by [Antonello Zanini](https://blog.logrocket.com/author/antonello-zanini/)✏️**

Email is an essential communication tool for both businesses and individuals, but creating well-designed and functional emails can be a challenging and time-consuming task for developers. Devs face several face pain points with regard to email creation, such as limited HTML support, compatibility issues across various email clients, and (worst of all) emails landing in spam folders. 

In an attempt to breathe new life into the way we write emails, the [Resend](https://resend.com/) team has built a library, [React Email](https://react.email), that promises to be a next-generation solution for crafting emails. React Email is made up of quality, unstyled components that enable us to create emails with React and TypeScript. This library offers a utility function to compile JSX code into email-friendly HTML that will render seamlessly across all popular email clients. 

In this article, we'll take a close look at React Email’s features, explore its styling options (including Tailwind CSS), and learn how to integrate Nodemailer to send emails directly from a Next.js app. We'll also compare React Email to [MJML](https://mjml.io/), a popular open source email solution. 

Here's a demo of the promotional email that we'll build for a florist site using React Email library: ![React Email Template Demo](http://blog.logrocket.com/wp-content/uploads/2023/06/react-email-template-demo.gif) 

_Jump ahead:_

*   [Prerequisites](#prerequisites)
*   [Why React Email?](#why-react-email)
*   [Setting up a Next.js project with React Email](#setting-up-nextjs-project-react-email)
*   [React Email components](#react-email-components)
*   [Styling in React Email](#styling-react-email)
    *   [Using a style object](#using-style-object)
    *   [Using Tailwind CSS](#using-tailwind-css)
*   [Designing the React Email template](#designing-react-email-template)
*   [Passing props in React Email](#passing-props-react-email)
*   [Integrating React Email with Nodemailer](#integrating-react-email-nodemailer)
*   [MJML vs React Email](#mjml-vs-react-email)

## Prerequisites

To follow along with this tutorial, you should have the following:

*   Node v.16 or higher installed on your machine
*   Familiarity with TypeScript
*   Familiarity with the Next.js framework
*   Experience with [Next.js app router](https://blog.logrocket.com/next-js-13-app-directory/)

## Why React Email?

React Email is a powerful tool that enables developers to streamline their processes for creating and sending emails. Here are some of React Email’s key features:

*   **Familiar syntax**: Developers who are already familiar with React will find React Email’s learning curve easy and will be able to start building emails right away. Many concepts from React transfer into React Email, like components and props
*   **Type safety with TypeScript**: React Email ships with TypeScript for adding static typing to code. This is helpful for catching errors earlier in development and for writing more robust code
*   **Ability to test emails during development**: React Email leverages the Resend API to allow test emails to be sent during development. This provides an opportunity to preview an email's appearance across different email clients and make necessary adjustments before sending
*   **Smooth integration with other email service providers**: React Email templates can easily be converted to email-friendly HTML and integrated with other email services like Nodemailer, SendGrid, or Resend
*   **Open source community**: React Email is a free and open source library and welcomes development contributions. The React Email community continually adds improvements, fixes bugs, and adds new features; its creators, [@ZenoRocha](https://twitter.com/zenorocha) and [@Bukinoshita](https://twitter.com/bukinoshita), are also supportive and active in the space

As with any new software, you may encounter bugs while using React Email. If you get stuck, feel free to reach out for help on the tool’s [Discord server](https://react.email/discord). You can also report bugs or submit an issue in the [React Email GitHub repository](https://github.com/resendlabs/react-email). 

Now, let's see how to integrate React Email into an existing Next.js project.

## Setting up a Next.js project with React Email <a name="setting-up-nextjs-project-react-email">

To set up a Next.js project with TypeScript, run the following command in your terminal:

```bash
npx create-next-app@latest --typescript <project-name>
```

This will launch a questionnaire to help you configure your application. I have opted in for the new app router and the `@/*` alias setting. I named my project `florist-app`, since we’ll be creating an email template for a fictional florist site. 

After the installation, cd into your project:

```bash
cd florist-app
```

Next, install React Email using any one of the following commands; I’ll be using npm:

```bash
# npm 
npx create-email@latest
# yarn
yarn create email
#pnpm
pnpm create email
```

After a successful installation, a new folder, `react-email-starter`, will be created within your project directory. This folder will include two subdirectories: `emails`, for storing React email templates, and `static`, for storing assets such as images. 

Now, move into this folder and install the project dependencies:

```bash
cd react-email-starter && npm install
```

Next, run the development server:

```bash
npm run dev
```

The above command will generate a `.react-email` folder specifically for email previews. This folder is also a Next.js app and will be installed with all necessary project dependencies. After installation, the preview application will be hosted on `localhost:3000`. 

_**N.B.,** when deploying your project, you'll want to omit `.react-email`; you can do this by adding it to your `.gitignore` file_ ![React Email Project](http://blog.logrocket.com/wp-content/uploads/2023/06/react-email-project.png) React Email provides some sample email templates to illustrate how emails are created. To test emails during development, use the **Send** button at the top right. 

In the `emails` folder, create a new file, `spring-sales.tsx`, for our demo marketing email. We’ll use this demo email to explore the major components of React Email.

## React Email components

There are currently 14 React components in the React Email library, including `Html`, `Image`, `Button`, `Heading`, `Link`, and more. You can view the [full list of components here](https://react.email/docs/components/html). In this tutorial, we’ll review a few of the components that you’re likely to use most regularly:

*   `Html`: React HTML component to wrap emails; all emails must be wrapped with this component
*   `Preview`: preview text that will be displayed in the recipient's inbox:

![Testing React Email](http://blog.logrocket.com/wp-content/uploads/2023/06/testing-react-email.jpeg)

*   `Image`: used to render images in your emails
*   `Link`: an anchor tag to link web pages, email addresses, or anything else a URL can address
*   `Section`: used to display a section that can later be formatted using `Column` and `Row`

Let’s explore the available options for adding custom styles to these components.

## Styling in React Email <a name="styling-react-email">

React Email offers two ways to style components — using a style object or using the Tailwind CSS `Tailwind` component. It's best not to rely on external (`<link rel="stylesheet">`) or embedded stylesheets (styles within the `<style>` tag above the `<body>`), because many email services cut out everything above the body tag and disable external style sheets. 

With that said, let’s go over React Email’s styling options.

### Using a style object <a name="using-style-object">

Similar to [CSS Modules](https://blog.logrocket.com/a-deep-dive-into-css-modules/), you can define styles using a JavaScript object in React. To do this, use the `style` prop and pass in an object with properties in camelCase representing the CSS properties and their corresponding values as strings:

```typescript
import { Button } from '@react-email/components'

export const Email = () => {
  return (
    <Html>
      <Button href="https://google.com" style={cta}>
        Click me
      </Button>
      {/* or */}
      <Text style={{ color: '#61dafb' }}>Confirm subscription</Text>
    </Html>
  );
}

const cta = {
  padding: '13px 20px',
  borderRadius: '5px',
  backgroundColor: '#D13928',
  textAlign: 'center' as const,
  color: '#fff',
  display: 'block',
  width: '45%',
  margin: '0.5rem auto 0 auto',
}

export default Email
```

If you use TypeScript, you may notice that some values give you a `Type 'string' is not assignable to type 'cssProperty'` error. You can address this error by adding `as const` at the end.

### Using Tailwind CSS

Another option for styling React Email components is to use Tailwind CSS. Import the `Tailwind` component into your email and use `tailwind` class as normal:

```typescript
import { Button } from '@react-email/button';
import { Tailwind } from '@react-email/tailwind';

const Email = () => {
  return (
    <Tailwind
      config={{
        theme: {
          extend: {
            colors: {
              brand: '#007291',
            },
          },
        },
      }}
    >
      <Button
        href="https://example.com"
        className="bg-brand px-3 py-2 font-medium leading-4 text-white"
      >
        Click me
      </Button>
    </Tailwind>
  );
}
```

There have been some [recent](https://github.com/resendlabs/react-email/discussions/630) [complaints about slow rendering](https://github.com/resendlabs/react-email/discussions/630) while using the `Tailwind` component. As a result, I've decided to use the style object approach for this tutorial. Let’s proceed to style our example florist email.

## Designing the React Email template <a name="designing-react-email-template">

Here’s the promotional email we’ll build for Petal Palace, a fictional florist site: ![Example React Email Template](http://blog.logrocket.com/wp-content/uploads/2023/06/example-react-email-template.png) Open the `spring-sales.tsx` file in `react-email-starter/emails` and add the markup below to create the above email design:

```typescript
#spring-sales.tsx
import {
  Html,
  Head,
  Preview,
  Body,
  Container,
  Link,
  Img,
  Text,
  Section,
  Hr,
  Row,
  Column,
} from '@react-email/components'

// configure path to assest folder 
const baseUrl = process.env.VERCEL_URL ? process.env.VERCEL_URL : '/static'

export const SpringSalesMail = () => (
  <Html>
    <Head />
    <Preview>Spring flower sales💐 Don't miss out!</Preview>
    <Body style={main}>
      <Container style={parentContainer}>
        <Link href='#' style={headingLink}>
          Petal Palace
        </Link>
        <Section style={heroSection}>
          <Img src={`${baseUrl}/banner.png`} style={banner} />
          <Link href='#' style={cta}>
            Get 33% off sale
          </Link>
        </Section>
        <Container style={container}>
          {/* more email markup */}
        </Container>
      </Container>
    </Body>
  </Html>
)

/* styling objects... */
export default SpringSalesMail
```

This is an abridged version showing only the important parts; you can view the full email markup in the [spring-sales.tsx file in the GitHub repo](https://github.com/Chinwike1/react-email-florist/blob/main/react-email-starter/emails/spring-sales.tsx). 

In the above code, we define a variable, `baseUrl`, that stores the path to the assets folder. This path can be `/static` or it can use your production environment’s URL — this is saved in the `VERCEL_URL` environment variable. 

We use this approach because when we deploy our project to Vercel, we can't access the `static` folder in the same way that we can during development. To solve this issue, we need to copy the email assets into the `public` folder at the root of our Next.js project. This makes the assets accessible in production through a URL like `https://your-production-url/asset.jpg`. 

Also, be sure to copy all environmental variables in `.env.local` to your Vercel settings. Here's how your `.env.local` should look:

```yaml
.env.local
VERCEL_URL=https://your-production-url
GMAIL_USER=yourEmail
GMAIL_PASSWORD=yourAppPassword
```

## Passing props in React Email <a name="passing-props-react-email">

In React Email, you can pass props just like you would in a regular React component. However, the purpose of these props is to render dynamic values, rather than to execute JavaScript operations. It's important to note that [executing JavaScript in emails is not recommended](https://stackoverflow.com/questions/3054315/is-javascript-supported-in-an-email-message), as it can lead to security vulnerabilities or even mark your emails as spam. 

Next, let’s create a TypeScript interface for the email props and use it within the body:

```typescript
# spring-sales.tsx
import {
  Html,
  /* other imports */
} from '@react-email/components'

// configure path to assest folder 
const baseUrl = process.env.VERCEL_URL ? process.env.VERCEL_URL : '/static'

interface SpringMailProps {
  userName: string
}

export const SpringSalesMail = ({ userName = 'Customer' }: SpringMailProps) => (
  <Html>
    {/* markup... */}
    <Container style={container}>
      <Text style={paragraph}>Dear {userName},</Text>
      {/* markup... */}
    </Container>
    {/* markup... */}
  </Html>
)

/* styling objects... */
export default SpringSalesMail
```

In the above code, we define a TypeScript interface, `SpringMailProps`, with a `userName` field. This interface specifies the structure of the props that our `SpringSalesMail` component is expecting to receive. If no user name is provided when the email is sent, the variable `userName` is initialized with a default value of "Customer". 

At this point, you can go ahead and send a test email to yourself. 

Now that our email template is built, we’re ready to integrate Nodemailer into our application. This will enable us to send emails to users.

## Integrating React Email with Nodemailer <a name="integrating-react-email-nodemailer">

Next.js offers a feature called [Route Handlers](https://blog.logrocket.com/using-next-js-route-handlers/) (previously called API Routes) that allows developers to create serverless functions that are automatically deployed on Vercel. Route Handlers can be used for all sorts of server-side tasks like querying a database. In our case, we‘ll use it to create a route handler that sends emails with Nodemailer, a popular open source library used to send emails from a server. 

First, let’s install the necessary dependencies:

```bash
# navigate to root and install dependencies
cd ..
npm i @react-email/render nodemailer
```

This will install the React Email’s `render` utility for compiling our React Email templates and the Nodemailer package. We’ll also need to grab the types for the Nodemailer package:

```bash
npm i --save-dev @types/nodemailer
```

Next, we’ll create an email route handler by creating a new folder called `email` in `app/api` and adding a `route.ts` file to it. This file will contain the logic for handling email requests:

```typescript
# app/api/email/route.ts
import nodemailer from 'nodemailer'
import { NextApiResponse } from 'next';
import { render } from '@react-email/render'
import { SpringSalesMail } from '../../../react-email-starter/emails/spring-sales'

export async function POST(req: Request, res: NextApiResponse){
  const { name, email } = await req.json()
  const emailHtml = render(SpringSalesMail({ userName: name }));
  // this will log the compiled email template created by React Email
  console.log(emailHtml)  
}
```

In this code snippet, we retrieve the `name` and `email` properties from the request body. After extracting the `name`, we pass it as a prop to the `SpringSalesMail` component. Then, we use the render utility to compile the marketing email and store the resulting HTML code in the `emailHtml` variable. Now, we can use Nodemailer’s `createTransport` and `sendEmail` functions to send emails from this route. 

For this example, we’ll be sending emails from a Gmail account. To follow along, you’ll need to log into your Google profile, enable two-factor authentication, and [generate an app password](https://support.google.com/accounts/answer/185833?hl=en). Be sure to store the password and your email address in `env.local` under `GMAIL_PASSWORD` and `GMAIL_USER`, respectively. 

Use the code below to send emails from this route:

```typescript
# app/api/email/route.ts
import nodemailer from 'nodemailer'
import { render } from '@react-email/render'
import { SpringSalesMail } from '../../../react-email-starter/emails/spring-sales'

export async function POST(req: Request) {
  const { name, email } = await req.json()
  const emailHtml = render(SpringSalesMail({ userName: name }))

  const transporter = nodemailer.createTransport({
    service: 'gmail',
    auth: {
      user: process.env.GMAIL_USER,
      pass: process.env.GMAIL_PASSWORD,
    },
    tls: {
      rejectUnauthorized: false,
    },
  })

  const mailOptions = {
    from: process.env.GMAIL_USER,
    to: email,
    subject: "Spring flower sales💐 Don't miss out!",
    html: emailHtml,
  }

  // ensure name and email are included
  if (!name || !email) {
    return new Response(
      JSON.stringify({ message: 'Please submit your name and email' }),
      { status: 400 }
    )
  }

  // send email
  transporter.sendMail(mailOptions, (error, info) => {
    if (error) {
      console.error(error)
      return new Response(
        JSON.stringify({ message: 'Error: Could not send email' }),
        { status: 400 }
      )
    }

    console.log('Email sent: ' + info.response)
    return new Response(
      JSON.stringify({ message: 'Email sent successfully' }),
      { status: 200 }
    )
  })
}
```

This code snippet creates an email transporter using Nodemailer's `createTransport()` function with an email service (in our case, Gmail) and authentication credentials stored in environment variables. The `tls` object is used to disable server certificate validation; this approach is just for testing purposes and is not recommended for production. 

The email details are specified in `mailOptions`, with the following fields:

*   `from`: set to the same email address stored in the `GMAIL_USER` environment variable
*   `to`: specifies the email address of the recipient — the `email` from the request
*   `subject`: the email subject line
*   `html`: contains the HTML content of the email, which is the React Email template stored in `emailHtml`.

The code checks if the `name` and `email` have been submitted. If so, the `sendMail()` function is called with `mailOptions`. If an error occurs, an error message is returned with a 400 “bad request”. If the email is sent successfully, a success message is returned with a 200 “request status”. 

Now, let’s test this functionality on the frontend! 

On the `app/page.tsx` homepage, use the code below to test the email route:

```typescript
# app/page.tsx
'use client'
import * as React from 'react'

export default function Home() {
  const [name, setName] = React.useState('')
  const [email, setEmail] = React.useState('')
  const [isSending, setIsSending] = React.useState(false)

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault()
    setIsSending(true)

    try {
      const response = await fetch('http://127.0.0.1:3000/api/email', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          name,
          email,
        }),
        mode: 'no-cors', // disable cors
      })
    } catch (error) {
      console.log(error)
      alert('Something went wrong. Please try again.')
    } finally {
      setIsSending(false)
    }
  }

  return (
    <main className='p-4'>
      <form onSubmit={handleSubmit}>
        <div className='my-2'>
          <label htmlFor='Name'>Name</label>
          <input
            type='text'
            className='text-black'
            value={name}
            onChange={(e) => setName(e.target.value)}
            required
          />
        </div>
        <div className='my-2'>
          <label htmlFor='Name'>Email</label>
          <input
            type='email'
            className='text-black'
            value={email}
            onChange={(e) => setEmail(e.target.value)}
            required
          />
        </div>
        <button
          type='submit'
          className='p-2 transition-colors bg-blue-500 rounded-sm disabled:bg-blue-300'
          disabled={isSending}
        >
          Send Email
        </button>
      </form>
    </main>
  )
}
```

And that’s the full gist of creating and sending emails with React Email! 

Now, let’s consider how React Email compares to [MJML](https://mjml.io/), a popular open source library in the email templating niche.

## MJML vs. React Email

[MJML](https://mjml.io) is an open source email framework that was created by Mailjet in 2015 to simplify the process of creating responsive email templates by providing prebuilt components — similar to React Email. MJML has a proven track record and has been tested across multiple email clients. 

Despite MJML’s popularity and longevity, there are several reasons why you might consider using a modern email templating solution, like React Email, instead of MJML. Let’s take a look.

### Developer experience

The React Email team advocates for and prioritizes improving developer experience. As a result, React Email is easy to use, integrates well with existing email service providers, and includes a provision for testing emails during development. The library was built with developers in mind which is always a win.

### Syntax

MJML’s syntax is difficult to scan through as every component is prefixed with `mj-`. Here’s a sample:

```html
<mjml>
  <mj-head />
  <mj-body>
    <mj-section>
      <mj-text>Hello World!</mj-text>
    </mj-section>
  </mj-body>
</mjml>
```

In contrast, React Email's syntax is much cleaner and, if you're familiar with JSX, the learning curve is easier.

### Compatibility with modern tooling

React Email was [created with the intention](https://react.email/docs/introduction) of rethinking and innovating the email creation and send processes. By integrating with modern tools like Next.js, SendGrid, and Tailwind, you can be certain that the library is future-proof.

### Open source community

React Email has a supportive community that is constantly adding new features, fixing bugs, and helping developers get started with the library. In contrast, MJML's community has dwindled recently. For example, its [previous solution](https://github.com/wix-incubator/mjml-react) for using MJML with React is now deprecated. 

MJML is still a solid choice for creating emails, particularly for creating responsive emails as React Email lacks in this department. Moreover, since React Email is new software, you may encounter more bugs. Your choice of email templating solution should ultimately depend on your project's needs.

## Conclusion

React Email is a modern tool that simplifies the email creation process by enabling React developers to leverage the power of React's component-based architecture. With React Email, developers can create beautiful and reusable email templates without dealing with cumbersome table syntax. 

Despite its many advantages, React Email still has room for improvement. However, the project has a promising [roadmap](https://react.email/docs/roadmap) that includes a VS Code extension for previewing emails. As a React developer, you can contribute to the growth of this project by providing feedback and contributing to the community. Even in its current state, React Email offers a great developer experience, and I am excited about its future potential. 

All of the code used in this article is available on [GitHub](https://github.com/Chinwike1/react-email-florist).

---

##Get setup with LogRocket's modern React error tracking in minutes:

1.Visit https://logrocket.com/signup/ to get an app ID.
2.Install LogRocket via NPM or script tag. `LogRocket.init()` must be called client-side, not server-side.

NPM:
```bash
$ npm i --save logrocket 

// Code:

import LogRocket from 'logrocket'; 
LogRocket.init('app/id');
```
Script Tag:
```javascript
Add to your HTML:

<script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script>
<script>window.LogRocket && window.LogRocket.init('app/id');</script>
```

3.(Optional) Install plugins for deeper integrations with your stack:
*   Redux middleware
*   ngrx middleware
*   Vuex plugin

[Get started now](https://lp.logrocket.com/blg/signup)