Leave Localhost logoLeave LocalhostDocs
Emails

Customizing Email Templates

All production email templates live in packages/backend/convex/email/templates/. They are React components rendered to HTML using @react-email/render.

All production email templates live in packages/backend/convex/email/templates/. They are React components rendered to HTML using @react-email/render.

Template Pattern

Auth and security templates receive appName from the required backend APP_NAME environment variable. Keep product names parameterized instead of hardcoding starter branding in template copy.

Every template follows a consistent pattern:

// 1. React Email imports
import { Body, Button, Container, Html, Text } from "@react-email/components";
import { render } from "@react-email/render";

// 2. Props interface
interface MyEmailOptions {
  name: string;
  actionUrl: string;
}

// 3. Inline styles (email clients don't support CSS files)
const bodyStyle = {
  backgroundColor: "#ffffff",
  fontFamily: '-apple-system, "Segoe UI", Roboto, sans-serif',
};

// 4. React component
export function MyEmail({ name, actionUrl }: MyEmailOptions) {
  return (
    <Html>
      <Body style={bodyStyle}>
        <Container>
          <Text>Hello {name}!</Text>
          <Button href={actionUrl}>Take action</Button>
        </Container>
      </Body>
    </Html>
  );
}

// 5. HTML render function
export function renderMyEmail(args: MyEmailOptions) {
  return render(<MyEmail {...args} />);
}

// 6. Plain text render function
export function renderMyEmailText({ name, actionUrl }: MyEmailOptions) {
  return `Hello ${name}!\n\nTake action: ${actionUrl}`;
}

Styling

Email templates use inline styles, not CSS classes. Email clients have limited CSS support, so inline styles ensure consistent rendering.

The existing templates share common style objects:

const bodyStyle = {
  backgroundColor: "#ffffff",
  fontFamily: '-apple-system, "Segoe UI", Roboto, sans-serif',
};

const textStyle = {
  color: "#111827",
  fontSize: "16px",
  lineHeight: "26px",
};

const buttonStyle = {
  backgroundColor: "#111827",
  borderRadius: "6px",
  color: "#ffffff",
  fontSize: "16px",
  fontWeight: "600",
  padding: "12px 18px",
};

const footerStyle = {
  color: "#6b7280",
  fontSize: "13px",
};

Creating a New Template

  1. Create the template file in packages/backend/convex/email/templates/:

    // myNewEmail.tsx
    export function MyNewEmail({ ... }) { ... }
    export function renderMyNewEmail(args) { return render(<MyNewEmail {...args} />); }
    export function renderMyNewEmailText(args) { return "..."; }
  2. Send it from a Convex function using the sendEmail facade:

    import { sendEmail } from "../email";
    import { renderMyNewEmail, renderMyNewEmailText } from "../email/templates/myNewEmail";
    
    const html = await renderMyNewEmail({ ... });
    const text = renderMyNewEmailText({ ... });
    await sendEmail(ctx, {
      to: "user@example.com",
      subject: "Subject line",
      html,
      text,
    });
  3. Always include plain text — the text field provides a fallback for email clients that don't render HTML.

Branding

To brand all emails:

  1. Replace "Leave Localhost" references in template copy and preview text.
  2. Update the button and body style colors to match your brand.
  3. Add your logo using the Img component (host the image on your domain or a CDN).

Previewing Changes

Use the React Email preview server for rapid iteration:

bun --cwd packages/email dev

Each file under packages/email/emails/ is a thin wrapper that imports a backend template and passes sample props — so the preview shows exactly what ships. When you add a new backend template, add a matching wrapper so it appears at :3003:

// packages/email/emails/my-new-email.tsx
import { MyNewEmail } from "@leavelocalhost/backend/convex/email/templates/myNewEmail";

export default function MyNewEmailPreview() {
  return <MyNewEmail name="Sample User" actionUrl="https://app.example.com/action" />;
}

Testing

After modifying templates, send a test email to yourself using the Resend dashboard or a Convex function to verify rendering across email clients (Gmail, Outlook, Apple Mail).

Next Reads

On this page