PayloadSolutions

Configuration

Every option of emailsPlugin(), what it defaults to, and when to change it.

emailsPlugin({
  emails: [welcome, passwordReset, invoicePaid],
})

emails is the only required option. Everything below has a default that suits a normal project.

Options

OptionDefaultWhat it does
emailsThe definitions. Duplicated slugs fail at startup.
templates{ default: DefaultTemplate }Your React Email components. See Templates.
collectionSlugtransactional-emailsRename if it collides with a collection of yours.
settings{}false removes the global entirely; otherwise { slug, adminRecipients, mediaCollection }.
logoff{ enabled, slug, retentionDays, storeHtml, storeVariables, includeTests }.
queueoff{ enabled, default, queue, retries } — deliver through Payload Jobs.
editoremail-safe LexicalReplace the editor used for the body and footer.
globalVariablessite/support/yearVariables every email gets.
globalVariableManifestmatching the aboveTheir chips and descriptions in the admin.
hooksshouldSend, beforeRender, beforeSend, afterSend.
accessany admin userread, update, delete.
versions{ drafts: true }false turns off versioning on the copy.
seedalwaysdevelopment or false.
validateInputdevelopmentalways or never.
dateFormatlong dateIntl.DateTimeFormatOptions for date variables.
disabledfalseKeep the collections (so migrations stay valid) but stop all runtime behaviour.

settings

settings: {
  adminRecipients: ['ops@example.com'],   // seeded on first run only
  mediaCollection: 'media',               // enables an upload field for a logo
  slug: 'email-settings',
}

settings: false removes the global. You then supply everything through globalVariables and the definitions' own to, and there is no footer.

globalVariables

Available in every email without any definition declaring them.

globalVariables: ({ settings, payload, locale }) => ({
  'site.name': settings.siteName ?? 'Acme',
  'site.url': settings.siteUrl ?? 'https://acme.com',
  'support.email': 'support@acme.com',
  'support.url': 'https://acme.com/help',
  year: new Date().getFullYear(),
}),
globalVariableManifest: {
  'support.url': { description: 'Help centre', type: 'url' },
},

Overriding globalVariables replaces the defaults entirely, so re-declare the ones you still want. The manifest is what editors see as the "available everywhere" chips — keep the two in step or a variable will work but not be discoverable.

log

log: {
  enabled: true,
  retentionDays: 90,
  storeHtml: false,      // the full rendered body — large, and personal data
  storeVariables: false, // the resolved values — same caveat
  includeTests: false,   // also log Send-a-test
}

The default records addressing and outcome, which is what support questions need. Turn the other two on while debugging, then off.

queue

queue: { enabled: true, default: false, queue: 'emails', retries: 3 }

default: true queues everything unless a call opts out. Requires something to run jobs.

versions

Drafts are on by default: an editor saves a draft, someone reviews, then publishes — and sending always uses the published version. versions: false removes versioning and every save is live.

seed

Controls the startup routine that creates missing documents, refreshes code-owned metadata, applies previousSlugs renames, and flags orphans.

  • always (default) — every boot, in every environment.
  • development — skipped in production, for teams that prefer a migration.
  • false — never; call payload.emails.sync() yourself.

It only ever creates copy. Existing copy is never touched.

validateInput

The structural check of input against inputSchema — required keys, primitive types, select options.

  • development (default) — a loud error where you will see it, no cost in production.
  • always — also in production, when input comes from somewhere you do not control.
  • never.

editor

The body editor is Lexical narrowed to what mail clients render. To change the feature set:

import { lexicalEditor } from '@payloadcms/richtext-lexical'
import { ButtonBlock, emailEditorFeatures } from '@payload-solutions/plugin-emails'

emailsPlugin({
  emails,
  editor: lexicalEditor({ features: [...emailEditorFeatures(), MyFeature()] }),
})

Keep ButtonBlock if you want buttons, and remember the plugin's link field carries a hook that stops {{url}} being percent-encoded — dropping it breaks variables inside links.

disabled

emailsPlugin({ emails, disabled: process.env.EMAILS_OFF === 'true' })

Collections, the global and the type hook stay, so your database schema and generated types do not change; the seed and payload.emails do not. Useful for a migration or a maintenance window.

Endpoints it adds

EndpointAuthPurpose
POST /api/<collection>/:id/previewadmin userRender one email with sample input.
POST /api/<collection>/:id/send-testadmin userSend that render to one address.
POST /api/email-templates/previewadmin userRender a template with placeholder copy.

All three require an authenticated admin-panel user and are used by the admin UI. Nothing the plugin adds is public.

On this page