Configure transactional email providers using split-package architecture
LaunchSaaS provides email support via a split-package design. The core @launchsaas/email package defines the interface and Email service; each provider is a separate package that you install only when needed.
Updated: 2026-03-15
Architecture
@launchsaas/email ← interface + Email class (always needed)
@launchsaas/email-resend ← Resend (recommended, works serverless)
@launchsaas/email-nodemailer ← Nodemailer (SMTP, Node.js only)You only add the provider package you actually use. This keeps your bundle lean and avoids pulling in SDKs you don't need.
LaunchSaaS ships with @launchsaas/email-resend as the default. In your
own app, install whichever provider you prefer — Resend, Nodemailer, or a
custom package.
Setting Up Email
1. Install the provider package
In your app directory pick one provider:
# Resend (recommended — works on serverless and edge)
pnpm add @launchsaas/email-resend
# Nodemailer / SMTP (Node.js runtime only)
pnpm add @launchsaas/email-nodemailer2. Add env variables
Add the provider's keys export to your app's env.ts so the env vars are validated at startup.
// src/env.ts
import { keys as emailKeys } from "@launchsaas/email-resend/keys";
export const env = createEnv({
extends: [
// ... other keys
emailKeys,
],
// ...
});Then set the variables in your .env:
# Resend
RESEND_API_KEY="re_..."
RESEND_FROM_EMAIL="[email protected]"
# — or — Nodemailer (SMTP)
SMTP_HOST="smtp.example.com"
SMTP_PORT="587"
SMTP_USER="[email protected]"
SMTP_PASS="your-password"
SMTP_FROM_EMAIL="[email protected]"
SMTP_SECURE="false"3. Register in src/capabilities.ts
Open your app's src/capabilities.ts and configure your chosen provider in the email field:
// src/capabilities.ts (your app — apps/launchsaas/src/capabilities.ts)
// Option A — Resend (recommended)
import { ResendEmailProvider } from "@launchsaas/email-resend";
// Option B — Nodemailer (SMTP)
// import { NodemailerEmailProvider } from "@launchsaas/email-nodemailer";
export const capabilities = {
// set to null to disable email
email: ResendEmailProvider.create(),
// email: NodemailerEmailProvider.create(),
// ...other capabilities
};4. Verify it works
Start your dev server and trigger an email (e.g., sign up to get a verification email). Check the Resend dashboard or your SMTP logs.
Resend: Domain Verification (Production)
To send from your own domain instead of the Resend test address:
- Go to Domains in the Resend Dashboard
- Click Add Domain and enter your domain (e.g.,
yourdomain.com) - Add the DNS records Resend provides (MX, TXT/SPF, DKIM) to your DNS provider
- Wait for propagation (up to 48 hours) then click Verify
- Update your env:
RESEND_FROM_EMAIL="[email protected]"
Email Templates
LaunchSaaS renders transactional emails with the shared @launchsaas/email-templates package in packages/design-system/email-templates. The apps/email app is only a React Email preview surface; it reuses the same shared layout instead of owning separate template implementations. The content of each production email is driven by translations in messages/[locale].json under the Email namespace:
| Template | When sent |
|---|---|
emailVerification | On user signup |
resetPassword | Password reset flow |
magicLink | Passwordless login |
paymentCompleted | Payment confirmation |
welcome | Post-verification welcome |
Customizing Content
Edit the text in messages/en.json (and messages/zh.json) under the Email key:
{
"Email": {
"hello": "Hello, {name}",
"emailVerification": {
"title": "Verify your email",
"heading": "Email Verification",
"action": "Verify Email",
"content": "Please click the button below to verify your email address."
}
}
}Previewing Templates
Run the email preview app to see all templates with hot reload:
pnpm run dev:email
# → http://localhost:3003Preview files live in apps/email/emails/. Each file provides mock data and renders the full email layout so you can iterate without sending real emails.
i18n Support
Templates automatically use the user's saved locale, falling back to the request locale, then en.
Custom Email Provider
To integrate a provider not shipped with LaunchSaaS, create a new email-xxx package:
1. Create the package
mkdir -p packages/capabilities/email-myservice/srcpackages/capabilities/email-myservice/package.json:
{
"name": "@launchsaas/email-myservice",
"version": "0.1.0",
"private": true,
"main": "./src/index.ts",
"exports": {
".": "./src/index.ts"
},
"dependencies": {
"@launchsaas/email": "workspace:*",
"@launchsaas/errors": "workspace:*",
"@t3-oss/env-nextjs": "^0.13.10",
"my-email-sdk": "^1.0.0",
"zod": "^4.0.0"
}
}packages/capabilities/email-myservice/src/keys.ts:
import { createEnv } from "@t3-oss/env-nextjs";
import { z } from "zod";
export const keys = createEnv({
server: {
MYSERVICE_API_KEY: z.string().optional(),
MYSERVICE_FROM_EMAIL: z.string().optional(),
},
experimental__runtimeEnv: {},
});packages/capabilities/email-myservice/src/provider.ts:
import type { EmailProvider, SendEmailOptions } from "@launchsaas/email";
export class MyServiceEmailProvider implements EmailProvider {
readonly name = "myservice";
static create(): MyServiceEmailProvider {
return new MyServiceEmailProvider();
}
async send(options: SendEmailOptions): Promise<void> {
// Call my-email-sdk here
}
}packages/capabilities/email-myservice/src/index.ts:
export { MyServiceEmailProvider } from "./provider";
export { keys } from "./keys";2. Register with pnpm workspace
Add to pnpm-workspace.yaml if it uses a glob that doesn't already cover it (e.g., packages/*).
3. Wire up in your app
pnpm add @launchsaas/email-myserviceAdd keys to env.ts and register in your app's src/capabilities.ts:
// src/capabilities.ts (apps/launchsaas/src/capabilities.ts)
import { MyServiceEmailProvider } from "@launchsaas/email-myservice";
export const capabilities = {
email: MyServiceEmailProvider.create(),
// ...other capabilities
};Disabling Email
To disable email entirely, set email: null in capabilities.ts. Email-dependent features (magic link, email verification) will be skipped automatically.
If you leave email disabled, also set magicLink: false in your features
config to avoid confusing users with a sign-in option that silently fails.
Provider Comparison
| Provider | Package | Runtime | Free tier |
|---|---|---|---|
| Resend | @launchsaas/email-resend | Any (HTTP) | 100/day |
| Nodemailer | @launchsaas/email-nodemailer | Node.js only | Depends on SMTP host |
| Custom | @launchsaas/email-xxx | Custom | — |