CoreMVP
Features

Email

Email functionality with Supabase Auth and optional Resend integration.

Overview

Email in this template is handled in two ways:

  • Supabase Auth emails - Verification and magic links (built-in)
  • Transactional emails - Custom emails via Resend (optional)

Supabase Auth Emails

Supabase Auth automatically sends emails for:

  • Email verification on signup
  • Magic link sign-in

Customizing Auth Email Templates

  1. Go to Supabase Dashboard → Authentication → Email Templates
  2. Customize the templates for each email type
  3. Use variables like {{ .ConfirmationURL }} for dynamic content

SMTP Configuration

For production, configure custom SMTP in Supabase:

  1. Go to Project Settings → Auth → SMTP Settings
  2. Enter your SMTP provider details (Resend, SendGrid, etc.)
  3. This allows emails to come from your domain

Adding Transactional Emails (Optional)

For custom emails (welcome, purchase confirmation), add Resend.

Install Resend

bun add resend

Add API Key

Add to .env.local:

RESEND_API_KEY=re_...

Create Email Service

Create a server-only email service:

src/services/email/service.ts
import "server-only";

import { Resend } from 'resend';

const resend = new Resend(process.env.RESEND_API_KEY);

export async function sendWelcomeEmail(to: string, name: string) {
  await resend.emails.send({
    from: 'noreply@yourdomain.com',
    to,
    subject: 'Welcome!',
    html: `<h1>Welcome, ${name}!</h1><p>Thanks for signing up.</p>`,
  });
}

export async function sendPurchaseConfirmation(to: string, amount: number) {
  await resend.emails.send({
    from: 'noreply@yourdomain.com',
    to,
    subject: 'Purchase Confirmed',
    html: `<p>Thank you for your purchase of $${amount}!</p>`,
  });
}

Verify Domain

Add DNS records in Resend dashboard for your sending domain.

Email Templates

For complex templates, use React Email:

bun add @react-email/components

Create email components:

src/services/email/templates/welcome.tsx
import { Html, Head, Body, Container, Heading, Text, Link } from '@react-email/components';

export function WelcomeEmail({ name }: { name: string }) {
  return (
    <Html>
      <Head />
      <Body>
        <Container>
          <Heading>Welcome, {name}!</Heading>
          <Text>We're excited to have you on board.</Text>
          <Link href="https://yourdomain.com/dashboard">Get Started</Link>
        </Container>
      </Body>
    </Html>
  );
}

Render and send:

import { render } from '@react-email/render';
import { WelcomeEmail } from './templates/welcome';

const html = render(<WelcomeEmail name={name} />);

await resend.emails.send({
  from: 'noreply@yourdomain.com',
  to,
  subject: 'Welcome!',
  html,
});

Sending on Events

After Signup

Call from the auth service or a database trigger:

src/services/auth/service.ts
export async function signUp({ email, password }: SignUpParams) {
  const supabase = await createSupabaseServerClient();
  const { data, error } = await supabase.auth.signUp({ email, password });

  if (data.user) {
    await sendWelcomeEmail(email, email.split('@')[0]);
  }

  return { ok: true, redirectPath: '/' };
}

After Purchase

Integrate with the webhook handler:

src/app/api/webhooks/stripe/route.ts
case 'checkout.session.completed':
  const session = event.data.object as Stripe.Checkout.Session;
  if (session.customer_email) {
    await sendPurchaseConfirmation(
      session.customer_email,
      (session.amount_total ?? 0) / 100
    );
  }
  break;

Testing

Resend free tier: 100 emails/day, 3,000/month. Sufficient for development.

For local testing without sending real emails:

export async function sendWelcomeEmail(to: string, name: string) {
  if (process.env.NODE_ENV === 'development') {
    console.log(`[EMAIL] Welcome email to ${to}: Hello ${name}`);
    return;
  }
  
  await resend.emails.send({ /* ... */ });
}

Was this page helpful?

On this page