How to build emails with React

And save a lot of time

Alberto Restifo
Building Lang.ai

--

A couple of weeks ago, our product manager announced what we’ll be focusing on the next couple of sprints: Emails. We want to make the automatically generated emails better. They will be responsive and have a more complex layout.

What does a web developer do when faced with such a task?

That’s right. You assign the story to a colleague and take 2 weeks off.

But maybe there is a way. Maybe emails can be a bit less painful to work with. At Séntisis we already used React to make PDF generation easier.

The same approach can be used with emails. We need to follow two steps only: coding the actual email HTML/CSS with React components and generate them in NodeJS.

Create the email markup

The basic idea is to take advantage of the server-side rendering abilities of React to get a compiled HTML file ready to be sent to the user.

I’ve created an example project. You can use it to bootstrap your own emails. All the code in this article is extracted from the project. Check it out on GitHub.

What the example project looks like

Use inline styles

Although nowadays more and more email clients support including styles in the <head>, to ensure the maximum compatibility, it’s recommended to use inline styles. We can do this directly with React.

Never reach for the DOM

You must always keep in mind that the email will only be rendered on the server. You won’t have access to the DOM.

Specifically, you can’t rely on:

  • componentDidMount
  • ref callbacks on elements
  • SVG images
  • Canvas

Create components to help you

Maybe the most annoying part of creating emails is writing all those nested tables.

To make our code cleaner (and thus more maintainable), we can create a Grid component to help us out with that:

Note: This is a simplified version of the one used in the example repository

This allows us to avoid writing a full table every time, hiding the complexity of the table layout from us:

Left side: React code. Right side: compiled output
For more complex usages see the example project

Make it responsive

Good news: all email clients which support media queries (now Gmail too) also support styles included in the <head>. Just recently Gmail added support too. This is great as we can’t write media queries with inline styles.

To make our Title heading smaller on mobile devices, we can just add a class to the element:

And then import a stylesheet in index.js:

It’s important we include the stylesheet in index.js and not directly in Title.js: CSS import is only available trough Webpack during development.

We’ll use a different strategy to include it in the compiled email. Everything inside index.js is only seen by Webpack.

Creating the email with Node.js

Before even considering to generate the email, we need to transpile the React files with Babel. If we try to require the component file straight away we’ll get errors. We need to get rid of JSX and ES6 imports first!

At Séntisis we build the emails as a separate git repository. We are then installing it as a module dependency with npm. I recommend this approach as it allows us to run the transpilation with the npm install hook.

Supposing we’re in a project that was created with create-react-app, we can just modify the package.json as follows:

The server folder contains simply two files:

server
├── createEmail.js # used as module entry point
└── email.html

The creation of the final email is pretty straightforward:

  • We get the HTML skeleton and the CSS to be inlined.
  • We render the React app.
  • We replace the template strings in the HTML skeleton with the rendered react element and the CSS.

A couple of things worth noticing:

  • When importing the main component of our email, we need to specify default as the transpiled file exports an object.
const Email = require('../lib/Email').default;
  • It’s better to use renderToStaticMarkup as it doesn’t include react-specific tokens in the generated HTML (such as the ids or comments). Including those might result in your email being sent to spam.
  • In the example, I’m using promises. You don’t have to. We already use promises elsewhere in our backend so it integrates better with our workflow.

Conclusions

There are no big libraries or complex snippets of code. We are just taking advantage or the amazing static rendering capabilities of React, combined with a bit of out-of-the-box thinking.

For us, it’s working well. We are now able to put more logic in the email templates, simplifying the backend code. The layouts are also significantly more maintainable, by hiding the maze of tables behind React components.

Remember to check out the example project on GitHub. There is much more to see. Fork it and make something awesome.

Worth reading?

Then it might be worth recommending. Give some claps to spread it! 👏

Check the other articles in our Building Lang.ai publication. We write about Machine Learning, Software Development, and our Company Culture..

--

--

I hike mountains, photograph landscapes, program computers and read books.