Logo VitNode
Documentation

Email

Email

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

plugins/blog/src/api/modules/newsletter/routes/welcome.route.ts
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.

`build()` and `deliver()` do exist

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

apps/api/src/vitnode.api.config.ts
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

apps/api/src/vitnode.api.config.ts
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.

Learn More