07/18/2016 Engineering Cody Reichert

TL;DR - This post discusses and provides code samples for how we use React components to create our email templates.

The beauty of React.js is in the reusability and composition of components. Components are encapsualted pieces of UI that can be composed to build larger interfaces. At Assertible we use React.js for pretty much everything that you see: website pages, the entire dashboard, our email templates, and even this blog!

Emails are tough, so this post hopes to give a few pointers on how you can apply style attributes (for example, <p style={{...}}>) as well as classes (eg, <p className="..") to your React components, which allows for consitent rendering across email clients. Note that a lot of the information below is specific to webpack and babel.

The problem with emails

If you've ever written email templates, you know the pain of trying to get a consistent output in different email clients. Some will recognize <style> tags, while others will strip them; some require inline styles while others we still have no idea what they want.

This post provides an approach for using React.js components to create a module that be rendered to static markup (renderToStaticMarkup) and used in emails while keeping a consistent render output across clients. I won't cover all the caveats of email client's here, but if you're interested MailChimp has a good overview on the topic.

The goal output

In order to have a good output across email clients, we want to have our final markup contain both style attributes and class attributes. The class attributes are optional, since all the styles are inline as well, but I've found that it prevents some corner cases and it doesn't add too much bloat.

Implementation

Now that we know we need both style attributes and classNames, we can come up with a goal before and after output for our markup.

This can be improved, of course, but ideally we can use the JSX spread operator to apply everything we need to an element at once. Something like this will work:

Before

<p {...style(['.text-muted', '.pull-xs-right']}>
  ...
</p>

The style function will take a list of 'classes' (classes that should be found within a CSS file -- more on that below), and apply both the styles and classes to our element:

After

<p class="text-muted pull-xs-right" style="color: gray; float: right">
 ...
</p>

With this approach we can guarantee a consistent render output across email clients. Gmail will be happy with the inline styles, and all the other clients will use whichever they prefer, classes or inline style attributes.

Let's go over the style function and what's happening:

style( Array<string> ): Object

The style function provides most of the functionality for applying the styles. Here is the entire code for style module (comments are important):

const bootstrap = require('/path/to/bootstrap.css')

export const applyStyle = (selectors, style = {}) => {
    // Our 'className' will be appended here
    let className = ""

    // For each 'selector' provided (eg '.pull-xs-right'), look up
    // the styles in the stylesheet. 'inline-style-loader', described
    // more below, is how we query the stylesheet.
    for(const key in selectors) {
        const slctr = selectors[key]
        const rules = bootstrap[slctr]
        // If CSS rules exist for this selector, add them to
        // 'className' and 'style'.
        if(rules) {
            const styles = rules.split(";")
            for (const key_ in styles) {
                const [attribute, value] = styles[key_].split(":")
                const attr = attribute.replace(/-([a-z])/g, g =>
                    g[1].toUpperCase()
                )
                // add key/value to the 'style' object
                style[attr.trim()] = value.trim()
            }
        }

        // Append selector to className
        className += ` ${slctr.substr(1)}`
    }

    // Return an object that can be spread directly onto a component.
    return {
        style,
        className
    }
}

If the comments don't provide enough info, the basic gist is: style takes an array of class names and returns an object with style and classNames ({ style, className }) that can be applied directly to a React component. The class names arguments are looked up in a style sheet (bootstrap in the example above) that is imported with inline-style-loader (discussed more below).

We now have a function that does this:

const styles = style(['.pull-xs-right'])
// {
//   className: "float:right;",
//   style: { float: 'right' },
// }

inline-style-loader

If you're wondering how we're able to query the CSS file as a JSON object to get access to styles, that is due to inline-style-loader. This webpack loader allows you to import a CSS file and access it as a JSON object.

One problem is that inline-style-loader will also format the styles to be used in a style _attribute (style="..."), so in the style function we have to to some string splitting.

inline-style-loader can be added to your webpack config like this:

module.exports = {
    // ... the rest of your config
    module: {
        loaders: [
            // ... the rest of your loaders
            {
                test: /bootstrap.css$/,
                loader: "raw!inline-style"
            },
        ]
    }
}

Wrapping up

Using this method has been successful for us so far. We use this for all of our emails sent from the dashboard as of now. Here's an example signup email rendered in Gmail:

Assertible Getting Started Email



Try it out

Teams and individuals use Assertible to test and monitor production web services, create scheduled assertions, and gain confidence in crucial infrastructure. Sign up for a free acount today.

Sign in with GitHub


Assuredly yours,
Cody Reichert

Categories

The easiest way to test and
monitor your web services

Define and test your web service with Assertible Track and test API deployments across environments Schedule API monitoring and failure alerts

Reduce bugs in web applications by using Assertible to create an automated QA pipeline that helps you catch failures & ship code faster.

Sign up for free