UNPKG

payload-plugin-newsletter

Version:

Complete newsletter management plugin for Payload CMS with subscriber management, magic link authentication, and email service integration

483 lines (385 loc) 11.6 kB
# Payload Newsletter Plugin [![npm version](https://img.shields.io/npm/v/payload-plugin-newsletter.svg?cache=300)](https://www.npmjs.com/package/payload-plugin-newsletter) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) A complete newsletter management plugin for [Payload CMS](https://github.com/payloadcms/payload) that provides subscriber management, magic link authentication, and email service integration out of the box. ## Features - 📧 **Complete Subscriber Management** - Ready-to-use subscriber collection with all essential fields - 🔐 **Magic Link Authentication** - Passwordless authentication for subscribers (separate from Payload auth) - 📨 **Email Service Integration** - Built-in support for Resend and Broadcast - 📅 **Newsletter Scheduling** - Schedule newsletters from your articles collection - ⚛️ **React Components** - Pre-built signup forms and preference management UI - 🌍 **Internationalization** - Multi-language support built-in - 📊 **Analytics Ready** - UTM tracking and signup metadata collection - ⚙️ **Admin UI Configuration** - Manage email settings through Payload admin panel ## Quick Start ### 1. Install the plugin ```bash bun add payload-plugin-newsletter # or npm install payload-plugin-newsletter # or yarn add payload-plugin-newsletter # or pnpm add payload-plugin-newsletter ``` ### 2. Add to your Payload config ```typescript import { buildConfig } from 'payload/config' import { newsletterPlugin } from 'payload-plugin-newsletter' export default buildConfig({ plugins: [ newsletterPlugin({ // Choose your email provider providers: { default: 'resend', // or 'broadcast' resend: { apiKey: process.env.RESEND_API_KEY, fromAddress: 'hello@yoursite.com', fromName: 'Your Newsletter', audienceIds: { en: { production: 'your_audience_id', development: 'your_dev_audience_id', }, }, }, }, }), ], // ... rest of your config }) ``` ### 3. That's it! 🎉 The plugin automatically adds: - A `subscribers` collection to manage your subscribers - A `newsletter-settings` collection for email configurations (supports multiple environments) - API endpoints for subscription and authentication - Newsletter scheduling fields to your articles (optional) ## Basic Usage ### Frontend Integration #### Simple Newsletter Signup Form ```tsx import { NewsletterForm } from 'payload-plugin-newsletter/components' export function MyHomepage() { return ( <NewsletterForm onSuccess={() => console.log('Subscribed!')} onError={(error) => console.error(error)} /> ) } ``` #### Custom Signup Form ```tsx async function handleSubscribe(email: string) { const response = await fetch('/api/newsletter/subscribe', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email }), }) if (!response.ok) { throw new Error('Subscription failed') } return response.json() } ``` ### Managing Subscribers Subscribers can be managed through the Payload admin panel at `/admin/collections/subscribers`. ### Email Settings After setup, configure email settings at `/admin/collections/newsletter-settings` in your admin panel. You can: - Create multiple configurations (e.g., for different environments or purposes) - Set one configuration as active at a time - Switch between email providers - Update API keys and settings - Customize email templates - Set subscription preferences **Note**: Only one configuration can be active at a time. The plugin will use the active configuration for sending emails. ## Initial Setup After installing the plugin, you'll need to: 1. **Create an email configuration**: - Go to `/admin/collections/newsletter-settings` - Click "Create New" - Give it a name (e.g., "Production" or "Development") - Configure your email provider settings - Set it as "Active" - Save 2. **Start collecting subscribers**: - Subscribers will appear in `/admin/collections/subscribers` - Use the provided React components or API endpoints ## Configuration Options ### Minimal Configuration ```typescript newsletterPlugin({ providers: { default: 'resend', resend: { apiKey: process.env.RESEND_API_KEY, fromAddress: 'newsletter@yoursite.com', fromName: 'Your Newsletter', }, }, }) ``` ### Full Configuration ```typescript newsletterPlugin({ // Subscriber collection slug (default: 'subscribers') subscribersSlug: 'newsletter-subscribers', // Email providers providers: { default: 'resend', resend: { apiKey: process.env.RESEND_API_KEY, fromAddress: 'newsletter@yoursite.com', fromName: 'Your Newsletter', audienceIds: { en: { production: 'aud_prod_123', development: 'aud_dev_123', }, es: { production: 'aud_prod_456', development: 'aud_dev_456', }, }, }, }, // Magic link authentication auth: { enabled: true, tokenExpiration: '7d', // How long magic links are valid magicLinkPath: '/newsletter/verify', // Where to redirect for verification }, // Features features: { // Lead magnets (e.g., downloadable PDFs) leadMagnets: { enabled: true, collection: 'media', // Which collection stores your lead magnets }, // Post-signup surveys surveys: { enabled: true, questions: [ { id: 'interests', question: 'What topics interest you?', type: 'multiselect', options: ['Tech', 'Business', 'Design'], }, ], }, // Newsletter scheduling for articles newsletterScheduling: { enabled: true, articlesCollection: 'posts', // Your articles/posts collection }, // UTM tracking utmTracking: { enabled: true, fields: ['source', 'medium', 'campaign', 'content', 'term'], }, }, // Internationalization i18n: { defaultLocale: 'en', locales: ['en', 'es', 'fr'], }, // Custom hooks hooks: { afterSubscribe: async ({ doc, req }) => { // Send to analytics, CRM, etc. console.log('New subscriber:', doc.email) }, }, }) ``` ## API Endpoints The plugin adds these endpoints to your application: ### POST `/api/newsletter/subscribe` Subscribe a new email address ```typescript // Request { "email": "user@example.com", "name": "John Doe", // optional "preferences": { // optional "newsletter": true, "announcements": false } } // Response { "success": true, "subscriber": { /* subscriber object */ } } ``` ### POST `/api/newsletter/verify-magic-link` Verify a magic link token ```typescript // Request { "token": "eyJhbGc..." } // Response { "success": true, "subscriber": { /* subscriber object */ }, "sessionToken": "eyJhbGc..." } ``` ### GET/POST `/api/newsletter/preferences` Get or update subscriber preferences (requires magic link auth) ### POST `/api/newsletter/unsubscribe` Unsubscribe an email address ## Newsletter Scheduling If you enable newsletter scheduling, the plugin adds scheduling fields to your articles collection: ```typescript features: { newsletterScheduling: { enabled: true, articlesCollection: 'articles', // Your existing collection } } ``` This adds a "Newsletter Scheduling" group to your articles with: - Schedule toggle - Send date/time picker - Audience segment selection - Send status tracking ## Email Providers ### Resend [Resend](https://resend.com) is a modern email API for developers. ```typescript providers: { default: 'resend', resend: { apiKey: process.env.RESEND_API_KEY, fromAddress: 'hello@yoursite.com', fromName: 'Your Newsletter', audienceIds: { en: { production: 'your_audience_id', }, }, }, } ``` ### Broadcast [Broadcast](https://sendbroadcast.net/) is a self-hosted email automation platform. ```typescript providers: { default: 'broadcast', broadcast: { apiUrl: 'https://broadcast.yoursite.com', tokens: { production: process.env.BROADCAST_TOKEN, development: process.env.BROADCAST_DEV_TOKEN, }, fromAddress: 'hello@yoursite.com', fromName: 'Your Newsletter', }, } ``` ## TypeScript The plugin is fully typed. Import types as needed: ```typescript import type { NewsletterPluginConfig, Subscriber, EmailProvider } from 'payload-plugin-newsletter/types' ``` ## Customization ### Custom Fields Add custom fields to the subscribers collection: ```typescript newsletterPlugin({ fields: { additional: [ { name: 'company', type: 'text', label: 'Company Name', }, { name: 'role', type: 'select', options: ['developer', 'designer', 'manager'], }, ], }, }) ``` ### Custom Email Templates Override the default email templates: ```typescript import { WelcomeEmail } from './emails/Welcome' newsletterPlugin({ templates: { welcome: WelcomeEmail, }, }) ``` ## Troubleshooting ### Common Issues **"Already subscribed" error** - The email already exists in the subscribers collection - Check the admin panel to manage existing subscribers **Magic links not working** - Ensure `JWT_SECRET` is set in your environment variables - Check that the `magicLinkPath` matches your frontend route **Emails not sending** - Verify your API keys are correct - Check the email provider's dashboard for errors - Ensure from address is verified with your provider ## Security ### Access Control Starting from v0.3.0, the plugin implements proper access control for all operations: - **Subscriber data**: Users can only access and modify their own data via magic link authentication - **Newsletter settings**: Only admin users can modify email provider settings and configurations - **API endpoints**: All endpoints respect Payload's access control rules #### Custom Admin Check The plugin supports multiple admin authentication patterns out of the box: - `user.roles.includes('admin')` - Role-based - `user.isAdmin === true` - Boolean field - `user.role === 'admin'` - Single role field - `user.admin === true` - Admin boolean If your setup uses a different pattern, configure a custom admin check: ```typescript newsletterPlugin({ access: { isAdmin: (user) => { // Your custom logic return user.customAdminField === true } }, // ... other config }) ``` ### Best Practices - Always use environment variables for sensitive data (API keys, JWT secrets) - Enable double opt-in for GDPR compliance - Configure allowed domains to prevent spam subscriptions - Set reasonable rate limits for subscriptions per IP ## Migration Guide Coming from another newsletter system? The plugin stores subscribers in a standard Payload collection, making it easy to import existing data: ```typescript // Example migration script const existingSubscribers = await getFromOldSystem() for (const subscriber of existingSubscribers) { await payload.create({ collection: 'subscribers', data: { email: subscriber.email, name: subscriber.name, subscriptionStatus: 'active', // Map other fields as needed }, }) } ``` ## Contributing We welcome contributions! Please see our [feedback and contribution guide](./FEEDBACK.md). ## License MIT