Create Email Templates with Pure React

Create Email Templates with Pure React

Learn how to create email templates with react and send them with SMTP.js

Hi everyone, in this article, we are going to create email templates with React components, this can be done in any react framework, the only requirement to do this is to have react-dom installed. The technologies used in this article are:

Also you can see the finished project here: GitHub Repo

Create the project.

As I already said you can use any react framework or bundler (vite, webpack, etc.) to do this, in my case I'm going to create a Next.js project.

 npx create-next-app --ts mail-template

Adding dependencies.

These dependencies are entirely optional, you can use your preferred packages.

  • Install Chakra UI:
npm i @chakra-ui/react @emotion/react@^11 @emotion/styled@^11 framer-motion@^4

Change your _app.tsx page to this:

import { ChakraProvider } from "@chakra-ui/react"

export default function MyApp({ Component, pageProps }) {
  return (
    <ChakraProvider>
      <Component {...pageProps} />
    </ChakraProvider>
  )
}
  • Install React Hook Form:
    npm i react-hook-form
    

Creating the template.

The template is nothing else but a React component, that's it, there is no magic, so let's create a directory called email at the root of our project, inside there create a file called EmailTemplate.tsx.

directory

Define the props

On the root of our project create a directory called types and create a file called Email.ts inside of it:

types

In my case, I'm only going to support the following props, but you can define what you want based on your needs:

Email.ts

Add the markup

This step is complete ought to you, create a different markup if you needed but for this example, I'm going to use this:

import { EmailData } from '@/types/Email'

export function EmailTemplate({ subject, name, email, message }: EmailData) {
  return (
    <body>
      <section
        style={{
          backgroundColor: '#1a202c',
          color: '#ffffff',
          padding: '2rem',
        }}
      >
        <h1 style={{ textAlign: 'center', textTransform: 'capitalize', marginTop: 0 }}>
          {subject}
        </h1>
        <h3>
          Name: <span style={{ color: '#81e6d9' }}>{name}</span>
        </h3>
        <h3>
          Email:{' '}
          <a href={`mailto:${email}`} style={{ color: '#81e6d9' }}>
            {email}
          </a>
        </h3>

        <article
          style={{
            backgroundColor: '#308c7a4c',
            color: '#81e6d9',
            borderRadius: '0.5rem',
            padding: '1rem',
          }}
        >
          <h3 style={{ margin: 0 }}>Message:</h3>
          <p style={{ margin: 0, fontSize: '1rem' }}>{message}</p>
        </article>
      </section>
    </body>
  )
}

As you can see I'm using inline styles, Why? This is because you need your styles already embedded on your markup, if you try to use a CSS file instead your styles won't be applied.

Configuring SMTP.js

Before we continue with the form component, we need to configure SMTP.js.

SMTP.js is a completely free solution to send emails from the frontend, but there is a caveat to consider, the maintainers have absolutely 0 spam tolerance, so you need to apply some measures before launching an app to production.

You need an SMTP server, if you don't have one, you can use your Gmail account, but you need to enable your account for "less secure apps".

Now you need to go to the SMTP.js website and click on:

encrypt

After that, you need to write your SMTP credentials, (if you are using Gmail, you can use the same exact values below).

smtp credentials

This will generate a token that we need to save on a .env.local file at the root of our project, create a variable, and paste the token there, also create another variable to store your email.

NEXT_PUBLIC_EMAIL_TOKEN="secret token"
NEXT_PUBLIC_EMAIL="my@email.com"

Install SMTP.js SDK

Now click download in SMTP.js website:

download smtp.js

And save the file inside the email directory that we created with a .ts extension:

smtp.js

Open the file and add on line 2 the comment // @ts-ignore, and replace var with export let:

convert SMTP.js to TS

Build the form.

As you may be already inferred, this is a simple form, 4 text fields, nothing special, so the following is the implementation of this simple form but with react-hook-form & Chakra UI.

So create the file under the directory components/forms/SendMail.tsx:

image.png

And add the code:

import { useCallback } from 'react'
import { renderToStaticMarkup } from 'react-dom/server'
import { Box, Center, Heading, VStack } from '@chakra-ui/layout'
import { FormControl, FormErrorMessage } from '@chakra-ui/form-control'
import { Input } from '@chakra-ui/input'
import { Textarea } from '@chakra-ui/textarea'
import { Button } from '@chakra-ui/button'
import { useToast } from '@chakra-ui/toast'
import { EmailIcon } from '@chakra-ui/icons'
import { SubmitHandler, useForm } from 'react-hook-form'

import type { EmailData } from '@/types/Email'
import { EmailTemplate } from '@/email/EmailTemplate'
import { Email } from '@/email/smtp'

export function SendMailForm() {
  const toast = useToast()
  const {
    handleSubmit,
    register,
    reset,
    formState: { errors, isSubmitting },
  } = useForm<EmailData>({ mode: 'onBlur' })

  const sendMail: SubmitHandler<EmailData> = useCallback(
    async data => {
      try {
        await Email.send({
          SecureToken: process.env.NEXT_PUBLIC_EMAIL_TOKEN,
          To: data.email,
          From: process.env.NEXT_PUBLIC_EMAIL,
          Subject: data.subject,
          Body: renderToStaticMarkup(<EmailTemplate {...data} />),
        })

        toast({
          title: 'Mail successfully sent',
          description: `The mail was sent to: ${data.email}`,
          status: 'success',
          position: 'top',
          duration: 5000,
          isClosable: true,
        })
      } catch (error) {
        console.error(error)
      } finally {
        reset()
      }
    },
    [reset, toast]
  )

  return (
    <Center h="100vh">
      <VStack spacing={7} align="stretch">
        <Heading as="h1" alignSelf="center">
          Send mail with SMTP.js
        </Heading>
        <Box maxW="lg" p="5" borderWidth="1px" borderRadius="lg" overflow="hidden">
          <form onSubmit={handleSubmit(sendMail)}>
            <VStack spacing={7} align="stretch">
              <FormControl isInvalid={errors.subject as boolean | undefined}>
                <Input
                  type="text"
                  id="subject"
                  placeholder="Subject"
                  {...register('subject', {
                    required: 'The subject is required',
                    minLength: { value: 4, message: 'Minimum length should be 4' },
                  })}
                />
                <FormErrorMessage>{errors.subject?.message}</FormErrorMessage>
              </FormControl>

              <FormControl isInvalid={errors.name as boolean | undefined}>
                <Input
                  type="text"
                  id="name"
                  placeholder="Name"
                  {...register('name', {
                    required: 'The name is required',
                    minLength: { value: 4, message: 'Minimum length should be 4' },
                  })}
                />
                <FormErrorMessage>{errors.name?.message}</FormErrorMessage>
              </FormControl>

              <FormControl isInvalid={errors.email as boolean | undefined}>
                <Input
                  type="email"
                  id="email"
                  placeholder="Email"
                  {...register('email', {
                    required: 'The email is required',
                    pattern: { value: /^\S+@\S+$/i, message: 'Write a valid email' },
                  })}
                />
                <FormErrorMessage>{errors.email?.message}</FormErrorMessage>
              </FormControl>

              <FormControl isInvalid={errors.message as boolean | undefined}>
                <Textarea
                  id="message"
                  placeholder="Write your message"
                  {...register('message', {
                    required: 'The message is required',
                    minLength: { value: 10, message: 'Minimum length should be 10' },
                  })}
                />
                <FormErrorMessage>{errors.message?.message}</FormErrorMessage>
              </FormControl>

              <Button
                type="submit"
                colorScheme="teal"
                size="md"
                loadingText="Submitting"
                isFullWidth={true}
                isLoading={isSubmitting}
                leftIcon={<EmailIcon />}
              >
                Send
              </Button>
            </VStack>
          </form>
        </Box>
      </VStack>
    </Center>
  )
}

Explanation.

Basically we are using SMTP.js SDK inside the sendMail function:

Email.send({
          SecureToken: process.env.NEXT_PUBLIC_EMAIL_TOKEN,
          To: data.email,
          From: process.env.NEXT_PUBLIC_EMAIL,
          Subject: data.subject,
          Body: renderToStaticMarkup(<EmailTemplate {...data} />),
 })

We are passing our secret token, email, and the subject, but the really important thing is in the Body:

renderToStaticMarkup(<EmailTemplate {...data} />)

renderToStaticMarkup is being imported from react-dom/server, this function receives the component which is the Email template that we did and returns a simple string with that markup as the content, that's when the email template is being created. Basically, what we did instead of writing a template string by ourselves, we created a simple React component and transpile it to a string with the tools that React already gives us by default.

To end this tutorial, we only need to import our form to the index page:

import Head from 'next/head'
import { SendMailForm } from '@/components/forms/SendMail'

export default function Home() {
  return (
    <>
      <Head>
        <title>Mail Sender</title>
        <meta name="description" content="Generated by create next app" />
        <link rel="icon" href="/favicon.ico" />
      </Head>

      <SendMailForm />
    </>
  )
}

Fill the form:

form

And check your inbox :D

image.png