Back to Blog
React Email: Ditching HTML Email's Dark Age for Modern Dev Practices
8 min readJun 16, 20269 views

React Email: Ditching HTML Email's Dark Age for Modern Dev Practices

Crafting HTML emails has always been a painful experience, stuck in a bygone era of table-based layouts and inline styles. React Email changes that, bringing modern component-based development and a great developer experience to a notoriously terrible part of our jobs.

FrontendWeb DevelopmentReactToolingFull-Stack
Share

by Sunil Band

The Dreaded HTML Email

Let's be honest, building emails is the worst. It's like stepping into a time machine set to 1999 web development. You're wrestling with <table> layouts, inline styles, and the constant fear that your beautifully crafted email will render completely broken in Outlook. Every time a marketing team asks for a new email template, a collective groan echoes through the developer halls. We're building sophisticated web applications with React, Next.js, and all sorts of modern tooling, yet our emails are still living in the dark ages. This impedance mismatch is not just annoying; it's a productivity killer.

We need a better way. We need to bring email development out of the primordial soup and into the modern era. That's where React Email comes in. It's not just another library; it's a paradigm shift, allowing you to compose rich, responsive email templates using the same component-based approach you use for your web applications.

Why React Email Matters

The fundamental problem with traditional HTML email development is the lack of abstraction and consistency. You're fighting against a fragmented rendering environment where every email client has its quirks. This leads to endless trial and error, manual HTML manipulation, and a mountain of boilerplate. React Email addresses this by providing a set of battle-tested components that abstract away these inconsistencies, allowing you to focus on the content and structure of your email.

Think about it: you get all the benefits of React development – component reusability, props for dynamic data, a clear component tree, and a familiar JSX syntax. This drastically reduces the cognitive load and development time. No more concatenating HTML strings or praying your templating engine escapes everything correctly. You're writing React, which means you're writing maintainable, testable code.

Getting Started with React Email

Setting up React Email is straightforward. It provides a CLI tool that helps you create a new project or integrate into an existing one. This CLI also gives you a development server with live reloading, so you can preview your emails as you build them – a massive improvement over sending test emails repeatedly.

First, let's create a new React Email project:

bash
npx create-email@latest

This command will scaffold a new project with a basic email template and the development server configured. You can then start the development server:

bash
npm run dev

Now, open http://localhost:3000 in your browser, and you'll see the React Email preview environment. This is where the magic happens – a dedicated UI to view and iterate on your email templates.

Building a Welcome Email Component

Let's create a simple welcome email. We'll use some of React Email's built-in components like Html, Head, Body, Container, Section, Text, and Button. These components are designed to render consistently across various email clients, handling all the nasty cross-client CSS issues for you.

Create a new file, emails/WelcomeEmail.tsx:

typescript
import * as React from 'react';
import { Html, Head, Body, Container, Section, Text, Button, Img, Hr, Column, Row } from '@react-email/components';

interface WelcomeEmailProps {
  userName: string;
  loginLink: string;
}

export const WelcomeEmail = ({ userName, loginLink }: WelcomeEmailProps) => (
  <Html lang="en">
    <Head>
      <title>Welcome to My Awesome App!</title>
      {/* You can add custom styles here if absolutely necessary, but generally React Email handles it */}
      <style>
        {`
          .custom-text {
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
            color: #333;
            line-height: 24px;
          }
        `}
      </style>
    </Head>
    <Body style={main}>
      <Container style={container}>
        <Section style={box}>
          <Img
            src="https://example.com/logo.png" // Replace with your actual logo URL
            width="170"
            height="50"
            alt="My Awesome App Logo"
            style={logo}
          />
          <Hr style={hr} />
          <Text className="custom-text" style={paragraph}>
            Hello {userName},
          </Text>
          <Text className="custom-text" style={paragraph}>
            Welcome to My Awesome App! We're thrilled to have you join our community.
          </Text>
          <Text className="custom-text" style={paragraph}>
            To get started, please click the button below to log in to your account:
          </Text>
          <Button style={button} href={loginLink}>
            Login to Your Account
          </Button>
          <Text className="custom-text" style={paragraph}>
            If you have any questions, feel free to reply to this email. We're always here to help!
          </Text>
          <Text className="custom-text" style={paragraph}>
            Best regards,
            <br />
            The My Awesome App Team
          </Text>
          <Hr style={hr} />
          <Text style={footer}>
            © 2023 My Awesome App. All rights reserved.
          </Text>
        </Section>
      </Container>
    </Body>
  </Html>
);

export default WelcomeEmail;

// Inline styles are often necessary for email clients, React Email helps manage them.
const main = {
  backgroundColor: '#f6f9fc',
  fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif',
};

const container = {
  backgroundColor: '#ffffff',
  margin: '0 auto',
  padding: '20px 0 48px',
  marginBottom: '64px',
};

const box = {
  padding: '0 48px',
};

const hr = {
  borderColor: '#e6ebf1',
  margin: '20px 0',
};

const logo = {
  margin: '0 auto',
  display: 'block',
};

const paragraph = {
  color: '#525f7f',
  fontSize: '16px',
  lineHeight: '24px',
  textAlign: 'left' as const,
};

const button = {
  backgroundColor: '#007EE6',
  borderRadius: '5px',
  color: '#fff',
  fontSize: '16px',
  fontWeight: 'bold',
  textDecoration: 'none',
  textAlign: 'center' as const,
  display: 'block',
  width: '100%',
  padding: '10px 0',
};

const footer = {
  color: '#8898aa',
  fontSize: '12px',
  lineHeight: '24px',
};

Notice how we're using props (userName, loginLink) just like any other React component. This makes our email dynamic and reusable. The styling is a mix of inline style objects (which React Email processes into inline CSS for compatibility) and a small style tag for custom CSS classes. React Email also handles the crucial step of inlining CSS and optimizing HTML for email clients upon build, which is a massive headache if you do it manually.

To see this email in the development preview, you'll need to update emails/index.tsx (or whatever your entry file is) to export WelcomeEmail.

typescript
// emails/index.tsx
export { WelcomeEmail } from './WelcomeEmail';
// Export other emails here...

Then, simply navigate to http://localhost:3000/welcome-email (or similar, depending on how your preview server maps names) in your browser to see it in action. You can even pass dummy props via the URL or modify the preview file in the .react-email directory to test different states.

Integrating with Your Backend

Once your email templates are ready, you'll need to integrate them with your backend to actually send them. React Email comes with a @react-email/render package that allows you to convert your React components into a static HTML string.

Here's an example using Node.js and Nodemailer, a popular library for sending emails:

typescript
import nodemailer from 'nodemailer';
import { render } from '@react-email/render';
import { WelcomeEmail } from './emails/WelcomeEmail'; // Adjust path as needed

async function sendWelcomeEmail(toEmail: string, userName: string) {
  const loginLink = 'https://your-app.com/login'; // Dynamic link generation would go here

  // Convert the React component to an HTML string
  const emailHtml = render(<WelcomeEmail userName={userName} loginLink={loginLink} />);

  // Create a Nodemailer transporter using your SMTP settings
  const transporter = nodemailer.createTransport({
    host: 'smtp.example.com',
    port: 587,
    secure: false, // Use 'true' if your SMTP provider uses SMTPS
    auth: {
      user: 'your_email@example.com',
      pass: 'your_email_password',
    },
  });

  // Send the email
  const mailOptions = {
    from: '"My Awesome App" <no-reply@your-app.com>',
    to: toEmail,
    subject: 'Welcome to My Awesome App!',
    html: emailHtml,
    // You can also provide a text version for clients that don't support HTML
    // text: `Hello ${userName}, Welcome to My Awesome App! ...`
  };

  try {
    const info = await transporter.sendMail(mailOptions);
    console.log('Email sent: %s', info.messageId);
  } catch (error) {
    console.error('Error sending email:', error);
  }
}

// Example usage:
sendWelcomeEmail('test@example.com', 'Jane Doe');

This workflow makes the backend integration trivial. You render the component, get the HTML, and pass it to your email sending service. The render function from @react-email/render takes care of all the necessary transformations, like inlining CSS, to ensure maximum compatibility.

Trade-offs and Considerations

While React Email is a huge leap forward, it's not a silver bullet, and there are a couple of things to keep in mind.

First, despite the React abstraction, you're still developing for the email client ecosystem. This means some modern CSS features or complex layouts might still be tricky or impossible to achieve reliably. React Email does an excellent job of abstracting away the worst of it, but don't expect to build a single-page application inside an email. Simplicity and robustness still win.

Second, the library provides a set of core components, but for highly custom designs, you might find yourself occasionally needing to drop down to raw HTML elements or applying custom inline styles. The good news is that React Email makes this manageable, and you can encapsulate these custom bits within your own reusable React components.

Finally, the local development server is fantastic for visual development, but you'll still need to test your emails across actual clients using services like Litmus or Email on Acid before sending them to your users. React Email significantly reduces the number of iterations you'll need, but it doesn't eliminate the need for final client testing.

Wrapping up

React Email is a game-changer for anyone tired of the archaic world of HTML email development. It brings the power, flexibility, and developer experience of modern React to a part of the stack that has been sorely neglected. By embracing components, a live-reloading development server, and robust rendering, it turns a painful chore into a manageable, even enjoyable, task.

My advice? The next time you need to build or refactor an email, give React Email a try. Scaffold a new project with npx create-email@latest, play around with the components, and experience what it feels like to develop emails in 2023. You'll likely never go back to raw HTML and inline style attributes again.

More from the blog
Available for projectsReady to make something fun 🎈

Ready to build the next system?Wanna build something awesome together?

Currently accepting high-impact opportunities in frontend engineering and scalable web applications.Got a cool idea rattling around? Let's grab a virtual coffee and turn it into something people love. ☕