Skip to content

Email development

If you are starting from an example project - all emails are installed by default. Therefore, you can continue reviewing the rest of the information.

Run a local email development server using React Email

We use React Email, a collection of high-quality, unstyled components for creating beautiful emails using React and TypeScript. React Email also has a CLI that is able to start a local development server for email development. Here's how to integrate it into your existing project.

bash
# Add dependencies
pnpm add react @react-email/components

# Add dev dependencies
pnpm add -D react-email

With these dependencies we will be able to create, develop and test our emails using React Email's development server and use their CLI to generate HTML files for our Supabase project.

Again, here are some commands you can add in your package.json for ease-of-use:

json
"scripts": {
  "email:dev": "email dev -p 4000",
  "email:export": "email export --outDir supabase/templates --pretty",
}

WARNING

Before you run any of these commands make sure to create an ./emails directory.

Set your email templates

bash
degit webscopeio/supabase-modules/apps/next/emails emails
ts
import * as React from "react"

export const main: React.CSSProperties = {
  backgroundColor: "#ffffff",
  fontFamily:
    '-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif',
}

export const container: React.CSSProperties = {
  margin: "0 auto",
  padding: "20px 12px 48px",
  maxWidth: "600px",
}

export const h1: React.CSSProperties = {
  color: "#09090B",
  fontSize: "24px",
  fontWeight: "bold",
  margin: "40px 0 16px",
  padding: "0",
}

export const buttonContainer: React.CSSProperties = {
  textAlign: "center",
}

export const button: React.CSSProperties = {
  backgroundColor: "#18181B",
  color: "#FAFAFA",
  fontWeight: 500,
  fontSize: "14px",
  padding: "10.5px 18px",
  borderRadius: "8px",
  width: "fit-content",
  textDecoration: "none",
  textAlign: "center",
  display: "block",
}

export const code: React.CSSProperties = {
  display: "inline-block",
  padding: "16px 4.5%",
  width: "90.5%",
  backgroundColor: "#F4F4F5",
  borderRadius: "5px",
  border: "1px solid #eee",
  color: "#09090B",
}

export const link: React.CSSProperties = {
  color: "#2754C5",
  fontSize: "14px",
  textDecoration: "underline",
}

export const text: React.CSSProperties = {
  color: "#09090B",
  fontSize: "14px",
  margin: "16px 0",
}

export const hr: React.CSSProperties = {
  borderColor: "#dfe1e4",
  margin: "24px 0",
}

export const footer: React.CSSProperties = {
  color: "#898989",
  fontSize: "12px",
  lineHeight: "22px",
  marginTop: "12px",
  marginBottom: "24px",
}
tsx
import * as React from "react"
import {
  Body,
  Button,
  Container,
  Head,
  Heading,
  Hr,
  Html,
  Link,
  Preview,
  Section,
  Text,
} from "@react-email/components"
import { EmailOtpType } from "@supabase/supabase-js"

import * as styles from "./_shared/styles"

const redirectTo = `/login`
const type: EmailOtpType = "signup"
const confirmationURL = `{{ .SiteURL }}/auth/confirm?token_hash={{ .TokenHash }}&type=${type}&next=${redirectTo}`

export default function Email() {
  return (
    <Html>
      <Head />
      <Preview>Confirm your signup</Preview>
      <Body style={styles.main}>
        <Container style={styles.container}>
          <Heading style={styles.h1}>Confirm your signup</Heading>
          <Text style={styles.text}>
            Almost there! Click below to confirm your signup:
          </Text>
          <Section style={styles.buttonContainer}>
            <Button style={styles.button} href={confirmationURL}>
              Click here to confirm
            </Button>
          </Section>
          <Text
            style={{
              ...styles.text,
              color: "#71717A",
            }}
          >
            If you didn&apos;t try to create an account, you can safely ignore
            this email.
          </Text>
          <Hr style={styles.hr} />
          <Text style={styles.footer}>
            <Link
              href="https://supabase-modules-docs.vercel.app"
              target="_blank"
              style={{ ...styles.link, color: "#71717A" }}
            >
              Supabase Modules
            </Link>
            <br />
            Build smarter with pre-built modules today
          </Text>
        </Container>
      </Body>
    </Html>
  )
}
tsx
import * as React from "react"
import {
  Body,
  Button,
  Container,
  Head,
  Heading,
  Hr,
  Html,
  Link,
  Preview,
  Section,
  Text,
} from "@react-email/components"
import { EmailOtpType } from "@supabase/supabase-js"

import * as styles from "./_shared/styles"

const redirectTo = `/login`
const type: EmailOtpType = "email_change"

const confirmationURL = `{{ .SiteURL }}/auth/confirm?token_hash={{ .TokenHash }}&type=${type}&next=${redirectTo}`

export default function Email() {
  return (
    <Html>
      <Head />
      <Preview>Confirm email</Preview>
      <Body style={styles.main}>
        <Container style={styles.container}>
          <Heading style={styles.h1}>Confirm email</Heading>
          {`{{ if .Email }}`}
          <Text style={styles.text}>
            Click below to confirm the update of your email from{" "}
            {`{{ .Email }}`} to {`{{ .NewEmail }}`}:
          </Text>
          {`{{ else }}`}
          <Text style={styles.text}>
            Click below to confirm your email to {`{{ .NewEmail }}`}:
          </Text>
          {`{{ end }}`}
          <Section style={styles.buttonContainer}>
            <Button style={styles.button} href={confirmationURL}>
              Click here to confirm
            </Button>
          </Section>
          <Text
            style={{
              ...styles.text,
              color: "#71717A",
            }}
          >
            If you didn&apos;t request an email change, you can safely ignore
            this email.
          </Text>
          <Hr style={styles.hr} />
          <Text style={styles.footer}>
            <Link
              href="https://supabase-modules-docs.vercel.app"
              target="_blank"
              style={{ ...styles.link, color: "#71717A" }}
            >
              Supabase Modules
            </Link>
            <br />
            Build smarter with pre-built modules today
          </Text>
        </Container>
      </Body>
    </Html>
  )
}
tsx
import * as React from "react"
import {
  Body,
  Button,
  Container,
  Head,
  Heading,
  Hr,
  Html,
  Link,
  Preview,
  Section,
  Text,
} from "@react-email/components"
import { EmailOtpType } from "@supabase/supabase-js"

import * as styles from "./_shared/styles"

const redirectTo = `/settings/accounts`
const type: EmailOtpType = "invite"
const confirmationURL = `{{ .SiteURL }}/auth/confirm?token_hash={{ .TokenHash }}&type=${type}&next=${redirectTo}`

export default function Email() {
  return (
    <Html>
      <Head />
      <Preview>You have been invited</Preview>
      <Body style={styles.main}>
        <Container style={styles.container}>
          <Heading style={styles.h1}>You have been invited</Heading>
          <Text
            style={styles.text}
          >{`You have been invited to create a user on {{ .SiteURL}}. Click below to accept the invite:`}</Text>
          <Section style={styles.buttonContainer}>
            <Button style={styles.button} href={confirmationURL}>
              Accept the invite
            </Button>
          </Section>
          <Hr style={styles.hr} />
          <Text style={styles.footer}>
            <Link
              href="https://supabase-modules-docs.vercel.app"
              target="_blank"
              style={{ ...styles.link, color: "#71717A" }}
            >
              Supabase Modules
            </Link>
            <br />
            Build smarter with pre-built modules today
          </Text>
        </Container>
      </Body>
    </Html>
  )
}
tsx
import * as React from "react"
import {
  Body,
  Container,
  Head,
  Heading,
  Hr,
  Html,
  Link,
  Preview,
  Text,
} from "@react-email/components"
import { EmailOtpType } from "@supabase/supabase-js"

import * as styles from "./_shared/styles"

const redirectTo = `/settings/accounts`
const type: EmailOtpType = "magiclink"

const confirmationURL = `{{ .SiteURL }}/auth/confirm?token_hash={{ .TokenHash }}&type=${type}&next=${redirectTo}`

export default function Email() {
  return (
    <Html>
      <Head />
      <Preview>Your One-Time password</Preview>
      <Body style={styles.main}>
        <Container style={styles.container}>
          <Heading style={styles.h1}>Your One-Time password</Heading>
          <Text style={styles.text}>Copy and paste this temporary code:</Text>
          <code style={styles.code}>{`{{ .Token }}`}</code>
          <Text style={styles.text}>
            You can also log in using this{" "}
            <Link href={confirmationURL} target="_blank" style={styles.link}>
              Magic Link
            </Link>
          </Text>
          <Text
            style={{
              ...styles.text,
              color: "#71717A",
            }}
          >
            If you didn&apos;t try to log in, you can safely ignore this email.
          </Text>
          <Hr style={styles.hr} />
          <Text style={styles.footer}>
            <Link
              href="https://supabase-modules-docs.vercel.app"
              target="_blank"
              style={{ ...styles.link, color: "#71717A" }}
            >
              Supabase Modules
            </Link>
            <br />
            Build smarter with pre-built modules today
          </Text>
        </Container>
      </Body>
    </Html>
  )
}
tsx
import * as React from "react"
import {
  Body,
  Button,
  Container,
  Head,
  Heading,
  Hr,
  Html,
  Link,
  Preview,
  Section,
  Text,
} from "@react-email/components"
import { EmailOtpType } from "@supabase/supabase-js"

import * as styles from "./_shared/styles"

const redirectTo = `/login/reset/new`
const type: EmailOtpType = "recovery"

const confirmationURL = `{{ .SiteURL }}/auth/confirm?token_hash={{ .TokenHash }}&type=${type}&next=${redirectTo}`

export default function Email() {
  return (
    <Html>
      <Head />
      <Preview>Reset your password</Preview>
      <Body style={styles.main}>
        <Container style={styles.container}>
          <Heading style={styles.h1}>Reset your password</Heading>
          <Text style={styles.text}>Click below to reset the password:</Text>
          <Section style={styles.buttonContainer}>
            <Button style={styles.button} href={confirmationURL}>
              Click here to continue
            </Button>
          </Section>
          <Text
            style={{
              ...styles.text,
              color: "#71717A",
            }}
          >
            If you didn&apos;t try to reset your password, you can safely ignore
            this email.
          </Text>
          <Hr style={styles.hr} />
          <Text style={styles.footer}>
            <Link
              href="https://supabase-modules-docs.vercel.app"
              target="_blank"
              style={{ ...styles.link, color: "#71717A" }}
            >
              Supabase Modules
            </Link>
            <br />
            Build smarter with pre-built modules today
          </Text>
        </Container>
      </Body>
    </Html>
  )
}

Set your CLI configuration to include your email templates

You can customize all of the authentication emails in your CLI configuration using the config.toml file. This guide is only showcasing the invite email but you can read more about the other available email templates in Customizing Email Templates.

toml
[auth.email]
# Allow/disallow new user signups via email to your project.
enable_signup = true
# If enabled, a user will be required to confirm any email change on both the old, and new email
# addresses. If disabled, only the new email is required to confirm.
double_confirm_changes = true
# If enabled, users need to confirm their email address before signing in.
enable_confirmations = true

[auth.email.template.invite]
subject = "You have been invited"
content_path = "./supabase/templates/invite.html"

Remember, you will need to reset or restart your containers whenever the CLI configuration changes.

Use Inbucket to capture emails sent from your local machine

Now you can use Inbucket to test the email templates, use different email addresses, see how the variables are being loaded into the emails and test the different authentication flows. You should be able to see both the generated HTML and Plain Text from your email templates.

These settings are only for local development. To update your hosted project, please copy the templates from supabase/templates into the Email Templates section of the Dashboard.

Keep in mind that for your production application you should update your SMTP configuration to use a service such as Resend. You can read more about this process in their guide Supabase with SMTP