Custom Emails with Supertokens, Resend, and React Email
Published: 2024-06-23 21:05:59

At [Cerebral Valley](https://cerebralvalley.ai) we use [Supertokens](https://supertokens.com/) 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](https://resend.com/) and [React Email](https://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](https://supertokens.com/) 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](https://resend.com/) account. Resend is a new platform to send emails programmatically. Think [Sendgrid](https://sendgrid.com/) but modern and easier to use. 

[Sign up] (https://resend.com/signup) 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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/25dm2n5j2aohgblheer0.png)
Add your API key to your `.env` file, something like
```
RESEND_API_KEY="<your_api_key>"
```

Then, add the domain you want to send your emails from. 
![Add domain in Resend](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7bxxz6qf6rdthsf1moqs.png)
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 };

```

## React Email setup

[React Email](https://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
```

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 };

```

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](https://supertokens.com/docs/thirdpartypasswordless/email-delivery/about#method-2-use-your-own-domain--smtp-server)). 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](https://supertokens.com/docs/thirdpartypasswordless/email-delivery/smtp/change-email-content).

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()
    ]
});
```

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](https://cerebralvalley.ai/slack). 

Check out what we're building at [Cerebral Valley](https://cerebralvalley.ai).