See the link below for an informal video covering the steps below and exploring the addition of a second renderer.
https://www.youtube.com/watch?v=1-ICr-J_YN0
api/src/lib/mailer.tsLet’s create our redwood mailer. Not all of this config below is needed as I’m trying to be verbose to cover the available options.
// mailer-core provides the Mailer itself
import { Mailer } from '@redwoodjs/mailer-core'
// These handler classes are who take your email content and send it out into the
// world. Think nodemailer for connecting to some SMTP server, amazon SES, etc.
import { InMemoryMailHandler } from '@redwoodjs/mailer-handler-in-memory'
import { NodemailerMailHandler } from '@redwoodjs/mailer-handler-nodemailer'
import { StudioMailHandler } from '@redwoodjs/mailer-handler-studio'
// The renderer is responsible for taking your react components and transforming
// them into valid email html/text which your handler will expect
import { ReactEmailRenderer } from '@redwoodjs/mailer-renderer-react-email'
// The normal redwood logger as you'd see in db.ts or auth.ts
import { logger } from './logger'
// We have to export a mailer variable which is an instance of the Mailer class.
// This class takes one config object as a constructor parameter
export const mailer = new Mailer({
// handling is dealing with your handlers
handling: {
// handlers is a key:value mapping of all the handlers you want to use
// the key can take anyname but we'll just use the handler name here.
handlers: {
inMemory: new InMemoryMailHandler(),
studio: new StudioMailHandler(),
nodemailer: new NodemailerMailHandler({
// We can see the nodemailer handler takes the typical parameters
// that nodemailer might need to make an SMTP connection
transport: {
host: 'localhost',
port: 4319,
secure: false,
},
}),
},
// we specify a default handler - this is our default in production!
default: 'nodemailer',
},
// rendering is dealing with your renderers
rendering: {
// again another key:value mapping just like handlers
renderers: {
reactEmail: new ReactEmailRenderer(),
},
// this is your default renderer - this isn't specific to prod/dev/test
default: 'reactEmail',
},
// test is used to provide some options that are relevant only when your
// mailer is in test mode
test: {
// the handler to use when in test mode
handler: 'inMemory',
// the condition to determine if we should be in test mode
// this could also be a sync function
when: process.env.NODE_ENV === 'test',
},
// similar to test above we specify development mode options
development: {
// the handler to use in development
handler: 'studio',
// when are we in development
// we should make this as wide as possible so we only reach production
// mode when we really want to be
when: process.env.NODE_ENV !== 'production',
},
// these are your default send options that will be used if you don't provide
// them at the time you go to send emails
defaults: {
replyTo: '[email protected]',
from: '[email protected]',
},
// the normal redwood logger is passed in
logger,
})
api/src/mail/**/*.(tsx|jsx)Let’s create an example email template using react-email and their components.
import React from 'react'
import {
Html,
Text,
Hr,
Body,
Head,
Tailwind,
Preview,
Container,
Section,
Img,
Heading,
Link,
} from '@react-email/components'
export function ContactUsEmail({
name,
email,
message,
when,
}: {
name: string
email: string
message: string
when: string
}) {
return (
<Html lang="en">
<Head />
<Preview>Message from: {email}</Preview>
<Tailwind>
<Body className="mx-auto my-auto bg-white font-sans">
<Container className="mx-auto my-[40px] rounded border border-solid border-gray-200 p-[20px]">
<Section className="mt-[32px]">
<Img
src={'<https://redwoodjs.com/images/logo.svg>'}
width="40"
height="37"
alt="RedwoodJS"
className="mx-auto my-0"
/>
</Section>
<Heading className="mx-0 my-[30px] p-0 text-center text-[24px] font-normal text-black">
New Message
</Heading>
<Text className="text-[14px] leading-[24px] text-black">
A user called “{name}” has sent the following message:
</Text>
<Container className="mx-0 my-0 bg-gray-100 px-4">
<Text className="text-[14px] text-black">{message}</Text>
</Container>
<Text className="text-[14px] leading-[24px] text-black">
They can be replied to by email at:{' '}
<Link
href={`mailto:${email}`}
className="text-blue-600 no-underline"
>
{email}
</Link>
</Text>
<Hr className="mx-0 my-[26px] w-full border border-solid border-[#eaeaea]" />
<Text className="text-[12px] leading-[24px] text-[#666666]">
Message was sent on {when}
</Text>
</Container>
</Body>
</Tailwind>
</Html>
)
}
We also have to update our tsconfig or jsconfig on the api side to include:
"jsx": "react-jsx"
It’s possible to view a live preview of your mail template from within studio and it’s “Template Preview” page. Here you can select the appropriate template and if needed the particular exported component or renderer. You can also pass props (limited to JSON) which your email component might take.

api/src/services/contacts/contacts.tsNow lets send an email in our service.
import { mailer } from 'src/lib/mailer'
import { ContactUsEmail } from 'src/mail/ContactUs'
...
...
...
export const createContact: MutationResolvers['createContact'] = async ({
input,
}) => {
const contact = db.contact.create({
data: input,
})
// Send an email
await mailer.send(
ContactUsEmail({
name: input.name,
email: input.email,
message: input.message,
when: new Date().toLocaleString(),
}),
{
to: '[email protected]',
subject: 'New contact form submission',
}
)
return contact
}