Documentation

Mailing

Learn how to send transactional emails in your Sushify Next.js application using React Email and various email providers

Sushify Next.js includes a powerful email system built with React Email that supports multiple email providers and offers beautifully designed, responsive email templates.

Features

  • React Email Templates: Build beautiful, responsive emails using React components
  • Multiple Providers: Support for Resend, Postmark, Mailgun, Plunk, and custom SMTP
  • Internationalization: Automatic email translation support
  • Type-Safe: Full TypeScript support with type-safe template parameters
  • Easy Provider Switching: Change email providers with a single environment variable
  • Preview Mode: Live preview your emails during development

Supported Providers

  • Resend - Modern email API (Recommended)
  • Postmark - Reliable transactional email
  • Mailgun - Enterprise email service
  • Plunk - Simple email API
  • Nodemailer - Custom SMTP support
  • Console - Development mode (logs emails to console)

Setup

1. Choose Your Email Provider

Select an email provider and configure it in your .env.local:

.env.local
# Email Configuration
MAIL_PROVIDER="resend" # Options: resend, postmark, mailgun, plunk, nodemailer, console
MAIL_FROM="noreply@yourdomain.com"

# Provider-specific API keys (choose one):

# Resend
RESEND_API_KEY="re_..."

# Postmark
POSTMARK_API_KEY="..."

# Mailgun
MAILGUN_API_KEY="..."
MAILGUN_DOMAIN="mg.yourdomain.com"

# Plunk
PLUNK_API_KEY="..."

# Nodemailer (SMTP)
SMTP_HOST="smtp.gmail.com"
SMTP_PORT="587"
SMTP_USER="your-email@gmail.com"
SMTP_PASSWORD="your-password"

2. Configure Mail Settings

Update your mail configuration in the config package:

packages/config/index.ts
export const config = {
  // ...
  mails: {
    from: process.env.MAIL_FROM || "noreply@yourdomain.com",
    provider: (process.env.MAIL_PROVIDER || "console") as MailProvider,
  },
};

Email Templates

The project includes pre-built email templates in packages/mail/emails/:

  • EmailVerification - Verify user email addresses
  • ForgotPassword - Password reset emails
  • MagicLink - Passwordless authentication
  • OrganizationInvitation - Team invitations
  • NewsletterSignup - Newsletter confirmations
  • LicenseRedemption - License key redemption
  • NewUser - Welcome emails for new users

Sending Emails

Using Templates

Send an email using a pre-built template:

import { sendEmail } from "@repo/mail";

await sendEmail({
  to: "user@example.com",
  templateId: "emailVerification",
  locale: "en",
  context: {
    username: "John Doe",
    verificationUrl: "https://yourdomain.com/verify?token=...",
  },
});

Custom Emails

Send a custom email without using a template:

import { sendEmail } from "@repo/mail";

await sendEmail({
  to: "user@example.com",
  subject: "Welcome to Our Platform",
  html: "<h1>Welcome!</h1><p>Thanks for joining us.</p>",
  text: "Welcome! Thanks for joining us.",
});

Creating Custom Templates

1. Create a New Template

Create a new React Email template in packages/mail/emails/:

packages/mail/emails/CustomEmail.tsx
import {
  Body,
  Container,
  Head,
  Heading,
  Html,
  Link,
  Preview,
  Text,
} from "@react-email/components";

interface CustomEmailProps {
  username: string;
  actionUrl: string;
  locale: string;
  translations: {
    greeting: string;
    callToAction: string;
  };
}

export default function CustomEmail({
  username,
  actionUrl,
  translations,
}: CustomEmailProps) {
  return (
    <Html>
      <Head />
      <Preview>{translations.greeting}</Preview>
      <Body style={main}>
        <Container style={container}>
          <Heading style={h1}>{translations.greeting}</Heading>
          <Text style={text}>
            Hello {username},
          </Text>
          <Link href={actionUrl} style={button}>
            {translations.callToAction}
          </Link>
        </Container>
      </Body>
    </Html>
  );
}

// Styles
const main = {
  backgroundColor: "#f6f9fc",
  fontFamily: '-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif',
};

const container = {
  backgroundColor: "#ffffff",
  margin: "0 auto",
  padding: "20px 0 48px",
  marginBottom: "64px",
};

const h1 = {
  color: "#333",
  fontSize: "24px",
  fontWeight: "bold",
  margin: "40px 0",
  padding: "0",
};

const text = {
  color: "#333",
  fontSize: "16px",
  lineHeight: "26px",
};

const button = {
  backgroundColor: "#5469d4",
  borderRadius: "4px",
  color: "#fff",
  fontSize: "16px",
  textDecoration: "none",
  textAlign: "center" as const,
  display: "block",
  padding: "12px",
};

2. Register the Template

Add your template to packages/mail/emails/index.ts:

export { default as CustomEmail } from "./CustomEmail";

3. Add Translations

Add translations in your locale files:

apps/web/locales/en/mail.json
{
  "customEmail": {
    "subject": "Your Custom Subject",
    "greeting": "Welcome to our platform!",
    "callToAction": "Get Started"
  }
}

4. Use Your Template

await sendEmail({
  to: "user@example.com",
  templateId: "customEmail",
  locale: "en",
  context: {
    username: "John Doe",
    actionUrl: "https://yourdomain.com/action",
  },
});

Development

Preview Emails

Run the email preview server to see your templates in the browser:

cd packages/mail
pnpm preview

This will start a development server at http://localhost:3005 where you can preview all your email templates with live reload.

Export Static HTML

Export all templates as static HTML files:

cd packages/mail
pnpm export

This generates HTML files in the packages/mail/out directory.

Provider Configuration

  1. Sign up at resend.com
  2. Create an API key
  3. Add your domain and verify DNS records
  4. Set environment variables:
MAIL_PROVIDER="resend"
RESEND_API_KEY="re_..."
MAIL_FROM="noreply@yourdomain.com"

Postmark

  1. Sign up at postmarkapp.com
  2. Create a server and get your API token
  3. Add and verify your sender signature
  4. Set environment variables:
MAIL_PROVIDER="postmark"
POSTMARK_API_KEY="..."
MAIL_FROM="noreply@yourdomain.com"

Mailgun

  1. Sign up at mailgun.com
  2. Add and verify your domain
  3. Get your API key
  4. Set environment variables:
MAIL_PROVIDER="mailgun"
MAILGUN_API_KEY="..."
MAILGUN_DOMAIN="mg.yourdomain.com"
MAIL_FROM="noreply@mg.yourdomain.com"

Custom SMTP (Nodemailer)

For custom SMTP servers (Gmail, Outlook, etc.):

MAIL_PROVIDER="nodemailer"
SMTP_HOST="smtp.gmail.com"
SMTP_PORT="587"
SMTP_USER="your-email@gmail.com"
SMTP_PASSWORD="your-app-password"
MAIL_FROM="your-email@gmail.com"

Console (Development)

For development, use the console provider to log emails instead of sending them:

MAIL_PROVIDER="console"

Error Handling

The mail package includes structured error handling:

import { sendEmail, MailError } from "@repo/mail";

try {
  await sendEmail({
    to: "user@example.com",
    templateId: "emailVerification",
    context: { /* ... */ },
  });
} catch (error) {
  if (error instanceof MailError) {
    console.error("Mail error:", error.code, error.message);
    console.error("Provider:", error.provider);
    console.error("Context:", error.context);
  }
}

Best Practices

  1. Use Templates: Always use templates for consistency and maintainability
  2. Test Locally: Use the preview server to test your emails before deployment
  3. Internationalization: Provide translations for all email content
  4. Responsive Design: Use React Email components to ensure mobile responsiveness
  5. Plain Text: Always include plain text versions for better deliverability
  6. Sender Verification: Verify your sender domain/email with your provider
  7. Rate Limiting: Implement rate limiting for user-triggered emails
  8. Error Logging: Log email errors for debugging and monitoring

API Reference

sendEmail()

function sendEmail<T extends TemplateId>(params: {
  to: string;
  locale?: string;
} & (
  | {
      templateId: T;
      context: TemplateContext<T>;
    }
  | {
      subject: string;
      text?: string;
      html?: string;
    }
)): Promise<boolean>;

Parameters:

  • to - Recipient email address
  • locale - (Optional) Locale for email translations
  • templateId - ID of the template to use
  • context - Template-specific context data
  • subject - Custom email subject (when not using template)
  • html - Custom HTML content (when not using template)
  • text - Custom plain text content (when not using template)

Returns: Promise<boolean> - true if email was sent successfully