Custom Emails with Supertokens, Resend, and React Email

Ivan Porollo - Jun 23 - - Dev Community

At Cerebral Valley we use Supertokens for authentication into our platform.

Supertokens comes with a default email template / design that is sent to users upon account creation, email confirmation, and other actions.

We wanted to customize emails sent out from Supertokens to our users to keep the brand aesthetic, so we used Resend and React Email to do so.

I wrote up this post to give step by step instructions of how to combine the three technologies and showcase their simplicity.

Pre-requisites

Your project will need to be using Supertokens as the method of authentication.

In this walkthrough, I am working out of a Typescript project with an Express backend.

Resend setup

First, you will need to create a Resend account. Resend is a new platform to send emails programmatically. Think Sendgrid but modern and easier to use.

Sign up and go through the onboarding flow to get an API key. By default, the first key that is created is called "Onboarding"
Resend API Keys
Add your API key to your .env file, something like

RESEND_API_KEY="<your_api_key>"
Enter fullscreen mode Exit fullscreen mode

Then, add the domain you want to send your emails from.
Add domain in Resend
You will have to add a few records to your domain which Resend will walk you through.

Next, create a file in your project called smpt.ts with the following contents

const smtpSettings = {
  host: 'smtp.resend.com',
  authUsername: 'resend',
  password: process.env.RESEND_API_KEY,
  port: 465,
  from: {
    name: '<your_email_sender_name>',
    email: '<your_email_account>',
  },
  secure: true,
};

export { smtpSettings };

Enter fullscreen mode Exit fullscreen mode

React Email setup

React Email is a library built by the founder of Resend. Since emails support HTML, the library allows you to customize emails with React components and then compiles them down to HTML before sending.

To install React Email, run

yarn add react @react-email/components @react-email/render
Enter fullscreen mode Exit fullscreen mode

Create your a file named Email.tsx for your React Email components. The file contents would be something like this

import * as React from 'react';
import { render } from '@react-email/render';
import {
  Body,
  Button,
  Container,
  Head,
  Heading,
  Hr,
  Html,
  Img,
  Link,
  Preview,
  Section,
} from '@react-email/components';

const emailSubject = 'Email Subject';

const EmailHtml = (content: string): string => {
  return render(<Email content={content} />);
};

// Component

interface EmailProps {
  content: string;
}

function Email(props: EmailProps): JSX.Element {
  const { content } = props;
  return (
    <Html>
      <Head />
      <Preview>{'What the user sees in the preview'}</Preview>
      <Body style={main}>
        <Container style={container}>
          <Img
            src={`https://yourlogo.com/logo.png`}
            width="42"
            height="42"
            alt="Logo"
            style={logo}
          />
          <Heading style={heading}>Click the button below</Heading>
          <Section style={buttonContainer}>
            <Button style={button} href={content}>
              My button
            </Button>
          </Section>
        </Container>
      </Body>
    </Html>
  );
}

// Styling

const logo = {
  borderRadius: 21,
  width: 42,
  height: 42,
};

const main = {
  backgroundColor: '#090909',
  fontFamily:
    '-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif',
  // ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"
};

const container = {
  margin: '0 auto',
  padding: '20px 0 48px',
  maxWidth: '560px',
};

const heading = {
  fontSize: '24px',
  letterSpacing: '-0.5px',
  lineHeight: '1.3',
  fontWeight: '400',
  color: '#fff',
  padding: '17px 0 0',
};

const paragraph = {
  margin: '0 0 15px',
  fontSize: '15px',
  lineHeight: '1.4',
  color: '#3c4149',
};

const buttonContainer = {
  padding: '27px 0 27px',
};

const button = {
  backgroundColor: '#fff',
  borderRadius: '3px',
  fontWeight: '600',
  color: '#000',
  fontSize: '15px',
  textDecoration: 'none',
  textAlign: 'center' as const,
  display: 'block',
  padding: '11px 23px',
};

const reportLink = {
  fontSize: '14px',
  color: '#b4becc',
};

const hr = {
  borderColor: '#dfe1e4',
  margin: '42px 0 26px',
};

export { EmailHtml, emailSubject };

Enter fullscreen mode Exit fullscreen mode

Note that the above code is for a showing a link in the email. Your content may be different.

Supertokens Config

Supertokens allows you to use your own domain / SMTP server (link to docs). We take advantage of this method to plug in Resend and the created React component.

In your Supertokens config, override the smtp settings as described in their docs here.

Don't forget to import your smtpSettings and Email component.

Your Supertokens config would look something like this

import supertokens from "supertokens-node";
import Passwordless from "supertokens-node/recipe/passwordless";
import Session from "supertokens-node/recipe/session";
import { SMTPService } from "supertokens-node/recipe/passwordless/emaildelivery";
import EmailVerification from "supertokens-node/recipe/emailverification"
import { SMTPService as EmailVerificationSMTPService } from "supertokens-node/recipe/emailverification/emaildelivery";
import { smtpSettings } from './smtp';
import { EmailHtml, emailSubject } from './Email';

supertokens.init({
    appInfo: {
        apiDomain: "...",
        appName: "...",
        websiteDomain: "..."
    },
    recipeList: [
       Passwordless.init({
         emailDelivery: {
          service: new SMTPService({
            smtpSettings,
            override: (originalImplementation): any => {
              return {
                ...originalImplementation,
                getContent: async function (input): Promise<any> {
                  const {
                    isFirstFactor,
                    codeLifetime, // amount of time the code is alive for (in MS)
                    email,
                    urlWithLinkCode, // magic link
                  } = input;

                  if (isFirstFactor) {
                    return {
                      body: EmailHtml(urlWithLinkCode),
                      isHtml: true,
                      subject: emailSubject,
                      toEmail: email,
                    };
                  } else {
                    return {
                      body: EmailHtml(urlWithLinkCode),
                      isHtml: true,
                      subject: emailSubject,
                      toEmail: email,
                    };
                  }
                },
              };
            },
          }),
        },
      }),

      Session.init()
    ]
});
Enter fullscreen mode Exit fullscreen mode

In the config above, we override the email delivery service with our own definition of the SMPTService, where we set the previously defined smptSettings and a new getContent function definition. In the new getContent, we get the defined React Component as the body of the email. The component is compiled into HTML, so we can use it in the email.

With those changes, you can start the server and run through the authentication flow. When you receive the email from Supertokens, you should see your new component design as the body of the email.

Wrapping up

If you're using Supertokens for your authentication, I highly recommend using Resend and React Email to customize your emails. It's very simple to setup and a pleasant developer experience.

If you have any questions, send me a DM in our community Slack.

Check out what we're building at Cerebral Valley.

. . . . . .