--- name: email-notifications description: Expert guide for sending transactional emails, creating templates, scheduling notifications, and email best practices. Use when implementing email functionality, notifications, or campaigns. --- # Email Notifications Skill ## Overview This skill helps you implement email notifications in your Next.js application. From transactional emails to scheduled campaigns, this covers everything you need for reliable email delivery. ## Email Providers ### Resend (Recommended for Next.js) **Install:** ```bash npm install resend ``` **Setup:** ```typescript // lib/email.ts import { Resend } from 'resend' export const resend = new Resend(process.env.RESEND_API_KEY) ``` **Send Email:** ```typescript // app/api/email/send/route.ts import { resend } from '@/lib/email' export async function POST(request: Request) { const { to, subject, html } = await request.json() try { const { data, error } = await resend.emails.send({ from: 'noreply@yourdomain.com', to, subject, html, }) if (error) { return Response.json({ error }, { status: 400 }) } return Response.json({ data }) } catch (error) { return Response.json({ error }, { status: 500 }) } } ``` ### SendGrid **Install:** ```bash npm install @sendgrid/mail ``` **Setup:** ```typescript // lib/sendgrid.ts import sgMail from '@sendgrid/mail' sgMail.setApiKey(process.env.SENDGRID_API_KEY!) export async function sendEmail({ to, subject, html, }: { to: string subject: string html: string }) { await sgMail.send({ from: 'noreply@yourdomain.com', to, subject, html, }) } ``` ### Nodemailer (SMTP) **Install:** ```bash npm install nodemailer ``` **Setup:** ```typescript // lib/nodemailer.ts import nodemailer from 'nodemailer' const transporter = nodemailer.createTransport({ host: process.env.SMTP_HOST, port: parseInt(process.env.SMTP_PORT!), secure: true, auth: { user: process.env.SMTP_USER, pass: process.env.SMTP_PASSWORD, }, }) export async function sendEmail({ to, subject, html, }: { to: string subject: string html: string }) { await transporter.sendMail({ from: process.env.EMAIL_FROM, to, subject, html, }) } ``` ## React Email (Email Templates) ### Install ```bash npm install react-email @react-email/components npm install -D @react-email/tailwind ``` ### Create Email Template ```typescript // emails/welcome.tsx import { Body, Button, Container, Head, Heading, Html, Link, Preview, Section, Text, Tailwind, } from '@react-email/components' interface WelcomeEmailProps { name: string loginUrl: string } export function WelcomeEmail({ name, loginUrl }: WelcomeEmailProps) { return ( Welcome to our platform, {name}! Welcome, {name}! We're excited to have you on board. Get started by logging in to your account.
If you didn't create this account, you can safely ignore this email. © 2024 Your Company. All rights reserved.
Unsubscribe
) } export default WelcomeEmail ``` ### Render Email Template ```typescript import { render } from '@react-email/components' import { WelcomeEmail } from '@/emails/welcome' import { resend } from '@/lib/email' export async function sendWelcomeEmail(name: string, email: string) { const html = render( ) await resend.emails.send({ from: 'welcome@yourdomain.com', to: email, subject: `Welcome, ${name}!`, html, }) } ``` ### Preview Emails (Development) ```bash # Add script to package.json { "scripts": { "email:dev": "email dev -p 3001" } } # Run preview server npm run email:dev # Visit http://localhost:3001 ``` ## Common Email Templates ### Password Reset ```typescript // emails/password-reset.tsx import { Body, Button, Container, Head, Heading, Html, Preview, Section, Text, Tailwind, } from '@react-email/components' interface PasswordResetEmailProps { name: string resetUrl: string } export function PasswordResetEmail({ name, resetUrl }: PasswordResetEmailProps) { return ( Reset your password Reset Your Password Hi {name}, we received a request to reset your password.
This link will expire in 1 hour. If you didn't request this, you can safely ignore this email.
) } ``` ### Order Confirmation ```typescript // emails/order-confirmation.tsx interface OrderConfirmationEmailProps { name: string orderId: string items: Array<{ name: string; price: number; quantity: number }> total: number } export function OrderConfirmationEmail({ name, orderId, items, total, }: OrderConfirmationEmailProps) { return ( Order confirmation #{orderId} Order Confirmed! Hi {name}, thanks for your order! Order #{orderId}
{items.map((item, index) => (
{item.quantity}x {item.name} ${item.price.toFixed(2)}
))}
Total ${total.toFixed(2)}
) } ``` ## Email Triggers ### Welcome Email on Signup ```typescript // app/api/auth/signup/route.ts import { sendWelcomeEmail } from '@/lib/emails/send' export async function POST(request: Request) { const { email, name } = await request.json() // Create user const user = await createUser({ email, name }) // Send welcome email (async, don't await) sendWelcomeEmail(name, email).catch((error) => { console.error('Failed to send welcome email:', error) }) return Response.json({ user }) } ``` ### Order Confirmation ```typescript // lib/emails/send.ts import { render } from '@react-email/components' import { OrderConfirmationEmail } from '@/emails/order-confirmation' import { resend } from '@/lib/email' export async function sendOrderConfirmation(order: Order) { const html = render( ) await resend.emails.send({ from: 'orders@yourdomain.com', to: order.customerEmail, subject: `Order Confirmation #${order.id}`, html, }) } ``` ## Scheduled Emails ### Daily Digest ```typescript // app/api/cron/daily-digest/route.ts import { sendDailyDigest } from '@/lib/emails/send' export async function GET(request: Request) { // Verify cron secret const authHeader = request.headers.get('authorization') if (authHeader !== `Bearer ${process.env.CRON_SECRET}`) { return new Response('Unauthorized', { status: 401 }) } // Get users who opted in for daily digest const users = await db.users.findMany({ where: { emailPreferences: { dailyDigest: true } }, }) // Send emails in batches for (const user of users) { await sendDailyDigest(user) } return Response.json({ sent: users.length }) } ``` ### Vercel Cron Configuration ```json // vercel.json { "crons": [ { "path": "/api/cron/daily-digest", "schedule": "0 9 * * *" }, { "path": "/api/cron/weekly-summary", "schedule": "0 9 * * 1" } ] } ``` ## Email Queue (Background Jobs) ### Using Supabase Edge Functions ```typescript // supabase/functions/send-email/index.ts import { serve } from 'https://deno.land/std@0.168.0/http/server.ts' import { Resend } from 'npm:resend' const resend = new Resend(Deno.env.get('RESEND_API_KEY')) serve(async (req) => { const { to, subject, html } = await req.json() const { data, error } = await resend.emails.send({ from: 'noreply@yourdomain.com', to, subject, html, }) return new Response(JSON.stringify({ data, error }), { headers: { 'Content-Type': 'application/json' }, }) }) ``` ### Queue Email for Sending ```typescript import { createClient } from '@/lib/supabase/server' export async function queueEmail({ to, subject, html, }: { to: string subject: string html: string }) { const supabase = createClient() await supabase.from('email_queue').insert({ to, subject, html, status: 'pending', created_at: new Date().toISOString(), }) } // Process queue with cron export async function processEmailQueue() { const supabase = createClient() const { data: emails } = await supabase .from('email_queue') .select('*') .eq('status', 'pending') .limit(10) for (const email of emails || []) { try { await resend.emails.send({ from: 'noreply@yourdomain.com', to: email.to, subject: email.subject, html: email.html, }) await supabase .from('email_queue') .update({ status: 'sent', sent_at: new Date().toISOString() }) .eq('id', email.id) } catch (error) { await supabase .from('email_queue') .update({ status: 'failed', error: error.message, attempts: email.attempts + 1, }) .eq('id', email.id) } } } ``` ## Email Preferences ### User Email Settings ```typescript // Database schema type EmailPreferences = { marketing: boolean productUpdates: boolean weeklyDigest: boolean orderUpdates: boolean } // Update preferences export async function updateEmailPreferences( userId: string, preferences: Partial ) { await db.users.update({ where: { id: userId }, data: { emailPreferences: preferences }, }) } // Check before sending export async function canSendEmail( userId: string, emailType: keyof EmailPreferences ): Promise { const user = await db.users.findUnique({ where: { id: userId }, select: { emailPreferences: true }, }) return user?.emailPreferences[emailType] ?? false } ``` ### Unsubscribe Link ```typescript // app/api/unsubscribe/route.ts export async function GET(request: Request) { const { searchParams } = new URL(request.url) const token = searchParams.get('token') // Verify token and get user const userId = await verifyUnsubscribeToken(token) // Unsubscribe from all marketing emails await db.users.update({ where: { id: userId }, data: { emailPreferences: { marketing: false, productUpdates: false, }, }, }) return new Response('You have been unsubscribed', { status: 200 }) } // Generate unsubscribe link function getUnsubscribeUrl(userId: string): string { const token = generateUnsubscribeToken(userId) return `${process.env.NEXT_PUBLIC_APP_URL}/api/unsubscribe?token=${token}` } ``` ## Email Testing ### Test Email Locally ```typescript // lib/email-test.ts import { sendWelcomeEmail } from '@/lib/emails/send' async function testEmail() { await sendWelcomeEmail('Test User', 'test@example.com') console.log('Test email sent!') } testEmail() ``` ### Email Preview in Browser ```typescript // app/email-preview/[template]/page.tsx import { WelcomeEmail } from '@/emails/welcome' export default function EmailPreview({ params, }: { params: { template: string } }) { const templates = { welcome: , // Add more templates } return (
{templates[params.template]}
) } ``` ## Email Analytics ### Track Email Opens ```typescript // emails/welcome.tsx export function WelcomeEmail({ name, userId }: { name: string; userId: string }) { const trackingPixel = `${process.env.NEXT_PUBLIC_APP_URL}/api/email/track/open?userId=${userId}&email=welcome` return ( {/* Email content */} ) } // app/api/email/track/open/route.ts export async function GET(request: Request) { const { searchParams } = new URL(request.url) const userId = searchParams.get('userId') const emailType = searchParams.get('email') // Track email open await db.emailEvents.create({ data: { userId, emailType, event: 'open', timestamp: new Date(), }, }) // Return 1x1 transparent pixel const pixel = Buffer.from( 'R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7', 'base64' ) return new Response(pixel, { headers: { 'Content-Type': 'image/gif', 'Cache-Control': 'no-store', }, }) } ``` ### Track Link Clicks ```typescript // Create tracked link function createTrackedLink(url: string, userId: string, emailType: string): string { return `${process.env.NEXT_PUBLIC_APP_URL}/api/email/track/click?url=${encodeURIComponent(url)}&userId=${userId}&email=${emailType}` } // Handle click tracking export async function GET(request: Request) { const { searchParams } = new URL(request.url) const url = searchParams.get('url') const userId = searchParams.get('userId') const emailType = searchParams.get('email') // Track click await db.emailEvents.create({ data: { userId, emailType, event: 'click', metadata: { url }, timestamp: new Date(), }, }) // Redirect to actual URL return Response.redirect(url!, 302) } ``` ## Best Practices Checklist - [ ] Use transactional email provider (Resend/SendGrid) - [ ] Create email templates with React Email - [ ] Implement unsubscribe links - [ ] Respect user email preferences - [ ] Queue emails for background processing - [ ] Add email tracking (opens, clicks) - [ ] Test emails before sending - [ ] Use proper from addresses - [ ] Include plain text version - [ ] Handle bounces and complaints - [ ] Add retry logic for failures - [ ] Monitor email delivery rates - [ ] Comply with email regulations (CAN-SPAM, GDPR) - [ ] Use email authentication (SPF, DKIM, DMARC) ## When to Use This Skill Invoke this skill when: - Setting up email notifications - Creating email templates - Implementing transactional emails - Building email campaigns - Setting up scheduled emails - Implementing email preferences - Debugging email delivery - Adding email tracking - Creating unsubscribe flows - Testing email templates