@fareszedney/payload-plugin-email-templates
v1.4.0
Published
Adds a configurable email-templates collection and reusable email rendering, syntax-validation, and sending utilities to a Payload 3 project.
Readme
Payload Email Templates Plugin
Adds a configurable email-templates collection and reusable email rendering, syntax-validation, and sending utilities to a Payload 3 project.
The plugin provides the template collection and sendTemplatedEmail helper. It
does not send email automatically: your Payload project must configure an
email adapter and call the helper from server-side application code, such as a
hook or endpoint.
Install
Install this package and a Payload email adapter in your Payload project. For SMTP, the Nodemailer adapter is one option:
pnpm add @fareszedney/payload-plugin-email-templates @payloadcms/email-nodemailerConfigure Payload
Register the plugin in your Payload config and configure an email adapter.
purposes defines the template types your application will use. The
variableResolver paths are also shown to the template designer.
import { buildConfig } from 'payload'
import { nodemailerAdapter } from '@payloadcms/email-nodemailer'
import { emailTemplatesPlugin } from '@fareszedney/payload-plugin-email-templates'
export default buildConfig({
// Keep your existing collections, database, and other config here.
email: nodemailerAdapter({
defaultFromAddress: process.env.SMTP_EMAIL_FROM_ADDRESS!,
defaultFromName: 'Example App',
transportOptions: {
host: process.env.SMTP_HOST,
port: Number(process.env.SMTP_PORT || 587),
auth: {
user: process.env.SMTP_USER,
pass: process.env.SMTP_PASSWORD,
},
},
}),
plugins: [
emailTemplatesPlugin({
purposes: [{ label: 'Receipt', value: 'receipts' }],
variableResolver: (purpose) =>
purpose === 'receipts' ? ['customer.name', 'order.number'] : [],
}),
],
})Set the SMTP environment variables for your provider. The helper passes
SMTP_EMAIL_FROM_ADDRESS as the message sender as well, so configure it to a
valid sender address for your email provider. You can use another Payload email
adapter instead; the adapter is configured by the consuming project, not by
this plugin.
Create a template
Start Payload and open the Email Templates collection in the admin panel.
Create and enable a template with the Receipt purpose. Design its content
using the available variable paths, for example customer.name and
order.number. The purpose value must match the value used in your call to
sendTemplatedEmail.
By default, templates can have static attachments from the media collection
and an option to attach a generated document. You can change the collection
slug, access rules, localization, attachment options, and other behavior using
the plugin options.
Send a templated email
Call the helper from server-side code where you have a Payload request, such as
an afterChange hook. The example below looks up the enabled receipt template
and sends it after an order is created:
import type { CollectionAfterChangeHook } from 'payload'
import { sendTemplatedEmail } from '@fareszedney/payload-plugin-email-templates'
export const sendReceiptEmail: CollectionAfterChangeHook = async ({
doc,
req,
operation,
}) => {
if (operation !== 'create' || !doc.email) return doc
const { docs } = await req.payload.find({
collection: 'email-templates',
where: {
and: [
{ purpose: { equals: 'receipts' } },
{ enabled: { equals: true } },
],
},
limit: 1,
})
const template = docs[0]
if (template) {
await sendTemplatedEmail({
req,
template,
expectedPurpose: 'receipts',
to: doc.email,
variables: {
customer: { name: doc.customerName },
order: { number: doc.number },
},
fallbackSubject: `Receipt for order ${doc.number}`,
})
}
return doc
}Add the hook to the relevant collection in your Payload config. Adapt the collection slug, recipient field, and variables to your generated Payload types and application schema.
sendTemplatedEmail renders the subject and HTML using the supplied
variables, and sends through req.payload.sendEmail. cc and bcc are
optional. It returns true when it sends and false when sending is skipped,
for example when the template is disabled, its purpose does not match, its HTML
is empty, or the recipient address is invalid.
Send a test email from the designer
The bundled designer includes a recipient field and Send test email button.
By default, the plugin registers an authenticated POST /api/email-templates/test
collection endpoint (under the Payload API route prefix) that sends the current
designer HTML through the consuming project's Payload email adapter. The button
appears only when the plugin endpoint is enabled.
Any authenticated user can send a test email by default. Because this can send
email to an arbitrary address, restrict it to trusted administrators with
testEmailAccess:
emailTemplatesPlugin({
purposes: [{ label: 'Receipt', value: 'receipts' }],
testEmailAccess: ({ req }) => req.user?.role === 'admin',
})Adapt the role check to your user schema and access policy. If your application
does not want a plugin-managed test endpoint, set testEmailEndpoint: false
and implement the test-send flow in your own project. You can customize the
collection endpoint suffix with a slash-prefixed testEmailEndpoint value; for
example, /preview registers POST /api/email-templates/preview.
Attachments
Static attachments are selected on the template and loaded from the configured
media collection. For relative media URLs, set PAYLOAD_PUBLIC_SERVER_URL to
the base URL from which the Payload server can fetch those files.
To attach a generated file, provide a generatedAttachments callback. It is
called only when the template's Attach Generated Document option is
enabled:
await sendTemplatedEmail({
req,
template,
expectedPurpose: 'receipts',
to: customer.email,
variables: { customer: { name: customer.name } },
fallbackSubject: 'Your receipt',
generatedAttachments: async () => [
{
filename: 'receipt.pdf',
content: receiptPdfBuffer,
contentType: 'application/pdf',
},
],
})The consuming project is responsible for generating that file and returning the attachment data.
Plugin options
purposes(required): purpose labels and values available to templates. At least one purpose is required.variableResolver: variable paths or variable definitions exposed to the designer for each purpose.variableDocs: richer variable documentation for the designer; takes precedence overvariableResolverwhen both are provided.collectionSlug,mediaCollection,labels,adminGroup: customize the collection and its admin presentation.access: access control for the templates collection. Define permissions appropriate to your project.localized: enable localization for template fields (defaults totrue).enableAttachmentsandenableGeneratedDocAttachment: enable their respective template fields (both default totrue).disabled: hide the collection and preventsendTemplatedEmailfrom sending (defaults tofalse).fieldComponent: replace the bundled admin template designer with a custom field component.testEmailEndpoint: set the collection-relative test endpoint suffix (defaults to/test) orfalseto disable the endpoint and designer button.testEmailAccess: authorize test sends; by default, any authenticated user may send one.
variableResolver and variableDocs make variable descriptions available to
the admin designer; the consuming application remains responsible for passing
the corresponding values to sendTemplatedEmail. Consumer-owned permissions,
notification orchestration, and generated-document creation remain in the
consumer project.
Development
Run pnpm build in this package to produce the published dist entry points.
