@onetodone/mailer
v0.3.1
Published
Type-safe transactional emails for Node.js: built-in templates, branding, i18n and pluggable transports, fully customizable.
Maintainers
Readme
Mailer from OneToDone
@onetodone/mailer sends transactional emails from Node.js applications. Configure your branding once (logo, company name, colors, footer) and send any email with a single typed call. The package ships with ready-made templates for common account flows. Every email renders to table-based HTML that holds up across major email clients and comes with a plain-text version. Texts are localizable, built-in templates can be restyled, reworded or replaced, and your own templates get the same typed API.

Screenshots of every email are in Built-in templates. The live preview shows every email in every built-in language.
Why @onetodone/mailer
- The emails, not just the tools to build them. Libraries such as React Email and MJML help you write email markup. This package ships the account emails themselves, already written, laid out and tested in major email clients.
- Ready for users in other languages. Built-in texts come in many languages, with plural forms and dates formatted per locale.
- Yours to change. Brand it from config, reword any text, or replace the layout and any template, all through the same typed API.
- Fits your stack. Send over SMTP, through Resend or with your own transport, with recipes for Better Auth, Auth.js, Express, Fastify, Next.js, NestJS and BullMQ.
Features
- Built-in templates for email verification, welcome emails, one-time codes, sign-in links, password reset, email address changes, security notices and account deletion.
- One call to send an email. Template names autocomplete, and props are checked at compile time and validated at runtime.
- Branding from configuration: logo, company name, colors, footer text and support address.
- Built-in texts in many languages. Override any text, or add a locale that falls back to English key by key. Right-to-left languages, such as Arabic and Hebrew, render right to left.
- Custom layouts and templates with the same typed API. Props are validated with any Standard Schema library, such as zod, valibot or arktype, and custom templates can bring their own texts per locale, plural forms included.
- Table-based HTML with inline styles and an Outlook button fallback, plus a plain-text version of every email.
- Attachments, such as invoices, and inline images through
cid:for pictures without a public URL, such as QR codes. - SMTP delivery through nodemailer, delivery through the Resend API with no extra dependencies, memory and console transports for tests and development, or a transport of your own. Delivery errors tell retryable failures, such as rate limits, from rejected emails.
- Safe defaults: interpolated values are escaped, links must be
http:orhttps:, header injection is rejected, and hook events never contain the email body or attachment content.
Requirements
- Node.js 22 or later. The main entry and the Resend transport also run on Bun, Deno and edge runtimes such as Cloudflare Workers, with no Node.js compatibility flags. SMTP delivery needs Node.js.
- ESM or CommonJS. Type declarations are included and need TypeScript 5.4 or later.
- Every built-in language ships in the main entry, so
locale: '<code>'needs no setup. The entry is about 250 kB of JavaScript (47 kB gzipped); each language adds about 2 kB gzipped. - nodemailer 6 or later, only for SMTP delivery through
@onetodone/mailer/smtp. Delivery through Resend with@onetodone/mailer/resendneeds no extra package.
Install
npm install @onetodone/mailerFor SMTP delivery, also install nodemailer:
npm install nodemailerQuick start
Create one mailer for your application:
// mailer.ts
import { createMailer } from '@onetodone/mailer'
import { smtpTransport } from '@onetodone/mailer/smtp'
export const mailer = createMailer({
transport: smtpTransport({
host: 'smtp.example.com',
port: 465,
secure: true,
auth: { user: process.env.SMTP_USER!, pass: process.env.SMTP_PASS! },
}),
from: 'My App <[email protected]>',
branding: {
companyName: 'My App',
appUrl: 'https://example.com',
supportEmail: '[email protected]',
},
})Then send emails from anywhere in your code:
import { mailer } from './mailer'
await mailer.send('verifyEmail', {
to: '[email protected]',
props: {
userName: 'Lizzie',
verifyUrl: 'https://example.com/verify?token=abc123',
expiresInMinutes: 1440,
},
})createMailer validates every setting when it is called. A typo or an invalid value throws a MailerError with code INVALID_CONFIG at startup, not on the first send.
Call await mailer.close() on shutdown to close the SMTP connections.
In CommonJS, use require:
const { createMailer } = require('@onetodone/mailer')
const { smtpTransport } = require('@onetodone/mailer/smtp')Documentation
- Built-in templates: when to send each template, and its props.
- Sending emails: send options, address forms, attachments and inline images, and rendering without sending.
- Configuration: every setting of
createMailer. - Customization: branding and theme, layout, and your own templates with their texts.
- Texts and locales: built-in languages, text overrides and your own locales.
- Transports: SMTP, Resend, console, memory and your own transport.
- Testing: tests with
memoryTransportandrender. - Recipes: Better Auth, Auth.js, Express, Fastify, Next.js, NestJS and background jobs.
- Hooks:
onSentandonErrorfor logging and metrics. - Errors:
MailerErrorcodes and retries. - Security: escaping, link and header checks, and email client support.
- Versioning and support: what counts as a breaking change, supported Node.js and TypeScript versions, and deprecations.
Versioning
The package follows semantic versioning. While the version is 0.x, breaking changes bump the minor version, so npm's default ^0.3.0 range stays within 0.3.x. Changes are listed in the changelog and in the GitHub releases. Versioning and support lists what counts as a breaking change and which Node.js and TypeScript versions are supported.
Contributing
Bug reports, fixes, new languages and translation fixes are welcome. See CONTRIBUTING.md, and report security problems privately as described in SECURITY.md.
