Send transactional emails with React Email templates, recipient-locale rendering, and pluggable delivery adapters.
VitNode provides a unified transactional email service accessible via c.get("email"). Emails are rendered using React components, localized to the recipient's language, and delivered through your chosen provider.
Quick start
Queue an email from a Hono route
import { buildRoute } from '@/api/builders'
export const sendWelcomeRoute = buildRoute({
route: {
method: 'post',
path: '/welcome',
responses: { 200: { description: 'Email sent' } },
},
handler: async (c) => {
const user = c.get('user')
await c.get('email').send({
user, // Resolves email address and preferred locale
subject: 'Welcome to VitNode!',
content: () => 'Thank you for joining our community.',
})
return c.json({ sent: true })
},
})send() renders the template and queues delivery. It keeps a successful API
response quick even when the mail provider is having a moody afternoon.
Use await c.get('email').build(args) followed by deliver(email) only when
a route truly needs immediate delivery. send(args) is the normal, queued
path.
deliver() takes an optional idempotencyKey and returns what the provider
reported, such as its message id:
const email = await c.get('email').build(args)
const { id } = await c.get('email').deliver(email, {
idempotencyKey: `order-receipt:${order.id}`, // same key on every retry
})Adapters whose provider deduplicates on a key, like Resend, forward it - so a retry after an unconfirmed send is not delivered twice. SMTP has no such feature and ignores it. Notification emails use this for every send.
Configuring Email Adapters
Configure your delivery transport in apps/api/src/vitnode.api.config.ts:
1. Resend Adapter
import { ResendEmailAdapter } from '@vitnode/core/api/adapters/email/resend'
export const vitNodeApiConfig = buildApiConfig({
email: {
from: 'noreply@yourdomain.com',
adapter: ResendEmailAdapter({
apiKey: process.env.RESEND_API_KEY!,
}),
},
})2. SMTP Adapter
import { SmtpEmailAdapter } from '@vitnode/core/api/adapters/email/smtp'
export const vitNodeApiConfig = buildApiConfig({
email: {
from: 'noreply@yourdomain.com',
adapter: SmtpEmailAdapter({
host: process.env.SMTP_HOST!,
port: Number(process.env.SMTP_PORT ?? 587),
user: process.env.SMTP_USER!,
password: process.env.SMTP_PASSWORD!,
}),
},
})Testing Email in AdminCP
Verify email configuration in the AdminCP at System → Integrations (/admin/core/system/integrations):
- Click Test Email on the email card.
- Enter an email address to dispatch an immediate test delivery.