Multi-provider transactional email with built-in templates. Configure once in lumenjs.email.ts, then send verification emails, password resets, and custom messages through SMTP, Resend, or SendGrid. With a Handlebars-like template engine and automatic auth integration.
Create a lumenjs.email.ts file at your project root:
// lumenjs.email.ts import type { EmailConfig } from '@nuraly/lumenjs/email'; export default { provider: 'resend', from: 'My App <[email protected]>', resend: { apiKey: process.env.RESEND_API_KEY, }, } satisfies EmailConfig;
Then send an email from any API route or loader:
// api/invite.ts import { sendEmail, loadEmailConfig } from '@nuraly/lumenjs/email'; export async function POST(req) { const config = await loadEmailConfig(process.cwd()); await sendEmail(config, { to: req.body.email, subject: 'You are invited!', html: '<h1>Welcome</h1><p>Click the link to join.</p>', }); return { sent: true }; }
fetch API.
LumenJS supports three email providers out of the box. Each is lazy-loaded. Only the provider you configure is imported.
SMTP: works with any SMTP server (Mailgun, Amazon SES, Postfix, etc.). Supports STARTTLS and AUTH LOGIN automatically:
export default { provider: 'smtp', from: 'App <[email protected]>', smtp: { host: 'smtp.mailgun.org', port: 587, secure: false, // true for port 465, false for STARTTLS on 587 auth: { user: process.env.SMTP_USER, pass: process.env.SMTP_PASS, }, }, } satisfies EmailConfig;
Resend: modern email API with excellent deliverability:
export default { provider: 'resend', from: 'App <[email protected]>', resend: { apiKey: process.env.RESEND_API_KEY, }, } satisfies EmailConfig;
SendGrid: enterprise-grade email delivery from Twilio:
export default { provider: 'sendgrid', from: 'App <[email protected]>', sendgrid: { apiKey: process.env.SENDGRID_API_KEY, }, } satisfies EmailConfig;
.env file for local development and your platform's secrets manager for production.
There are two ways to send email: the one-shot sendEmail() function, and the reusable createEmailSender() factory.
sendEmail(config, message): creates a new provider instance per call. Best for infrequent sends (webhooks, one-off notifications):
import { sendEmail, loadEmailConfig } from '@nuraly/lumenjs/email'; const config = await loadEmailConfig(process.cwd()); await sendEmail(config, { to: '[email protected]', subject: 'Order confirmed', html: '<p>Your order #1234 has been confirmed.</p>', text: 'Your order #1234 has been confirmed.', // optional - auto-generated from HTML if omitted });
createEmailSender(config): creates the provider once and reuses it. Best for high-throughput or repeated sends:
import { createEmailSender, loadEmailConfig } from '@nuraly/lumenjs/email'; const config = await loadEmailConfig(process.cwd()); const send = createEmailSender(config); // Provider is initialized on first call, reused after await send({ to: '[email protected]', subject: 'Hello', html: '<p>Hi Alice</p>' }); await send({ to: '[email protected]', subject: 'Hello', html: '<p>Hi Bob</p>' });
The EmailMessage interface:
| Field | Type | Description |
|---|---|---|
to | string | Recipient email address |
subject | string | Email subject line |
html | string | HTML body |
text | string? | Plain text fallback (auto-generated from HTML if omitted) |
LumenJS ships three built-in templates (verify-email, password-reset, welcome) and a Handlebars-like template engine for custom emails. Override built-in templates with file-based HTML or programmatic functions.
See Email Templates for the full template engine docs, file-based templates, and custom template functions.
When both the auth and email plugins are configured, LumenJS automatically wires them together. No manual setup required.
my-project/ lumenjs.config.ts ← title: 'My App' lumenjs.auth.ts ← native auth with requireEmailVerification: true lumenjs.email.ts ← any provider configured
When this setup is detected, LumenJS will:
verify-email template to the new user's address with a signed verification token URLpassword-reset template with a time-limited reset URLThe auto-wiring uses the appName from your lumenjs.config.ts title and the email subject lines follow the format:
"Verify your email - My App" "Reset your password - My App"
onEvent handler. If you define onEvent in your auth config, you take full control of email sending and the auto-wiring is skipped.
If auto-wiring is active, you will see this in your dev server console:
[LumenJS] Email auto-wired with auth module
To customize the emails sent by auth while keeping auto-wiring, override the templates via file-based templates (emails/verify-email.html) or the templates config option. The auto-wiring respects the template resolution order.
All options available in lumenjs.email.ts:
| Option | Type | Required | Description |
|---|---|---|---|
provider | 'smtp' | 'resend' | 'sendgrid' | Yes | Email delivery provider |
from | string | Yes | Sender address (e.g. 'App <[email protected]>') |
smtp.host | string | SMTP only | SMTP server hostname |
smtp.port | number | SMTP only | SMTP server port (587 for STARTTLS, 465 for SSL) |
smtp.secure | boolean | No | Use direct TLS (true for port 465). Default: false (STARTTLS) |
smtp.auth.user | string | SMTP only | SMTP authentication username |
smtp.auth.pass | string | SMTP only | SMTP authentication password |
resend.apiKey | string | Resend only | Resend API key |
sendgrid.apiKey | string | SendGrid only | SendGrid API key |
templates | EmailTemplates | No | Custom template functions keyed by template name |