The email and notification system is designed to be easily extensible. You can create custom templates, add new notification types, and enhance the system to meet your specific needs.
Perfect for: Custom business logic, brand-specific emails, new notification types, and extending the system functionality.
Follow the existing pattern when creating new templates in src/lib/emails/:
// Example: custom-welcome-email.tsx
import {
Body,
Container,
Head,
Html,
Link,
Preview,
Section,
Tailwind,
Text,
} from 'react-email'
import { getTranslations } from 'next-intl/server'
import { Fragment } from 'react'
// Define props interface
type CustomWelcomeEmailProps = {
userName: string
companyName: string
dashboardUrl: string
}
// Preview props for development
CustomWelcomeEmail.PreviewProps = {
userName: 'John Doe',
companyName: 'Acme Corp',
dashboardUrl: 'https://app.example.com/dashboard',
} as CustomWelcomeEmailProps
export default async function CustomWelcomeEmail({
userName,
companyName,
dashboardUrl,
}: CustomWelcomeEmailProps) {
// Load translations
const t = await getTranslations('email.custom.welcome')
return (
<Html>
<Head />
<Tailwind>
<Fragment>
<Preview>{t('preview')}</Preview>
<Body className="mx-auto my-auto bg-white px-2 font-sans">
<Container className="mx-auto my-[40px] max-w-[465px] rounded border border-solid border-[#eaeaea] p-8">
<Text className="text-2xl font-bold text-black">
{t('title', { companyName })}
</Text>
<Section className="my-4">
<Text className="text-base">
{t('greeting', { userName })}
</Text>
<Text className="text-base">
{t('content')}
</Text>
<Link
className="text-sky-500 hover:cursor-pointer hover:underline"
href={dashboardUrl}
>
{t('ctaButton')}
</Link>
</Section>
<Text className="text-sm leading-6 text-gray-500">
{t('footer')}
</Text>
</Container>
</Body>
</Fragment>
</Tailwind>
</Html>
)
}Use the established Tailwind classes from existing templates:
// Container
<Container className="mx-auto my-[40px] max-w-[465px] rounded border border-solid border-[#eaeaea] p-8">
// Typography
<Text className="text-2xl font-bold text-black">
<Text className="text-base">
<Text className="text-sm leading-6 text-gray-500">
// Links
<Link className="text-sky-500 hover:cursor-pointer hover:underline">Set up translation keys following the namespace pattern:
// In your translation files
{
"email": {
"custom": {
"welcome": {
"preview": "Welcome to our platform!",
"title": "Welcome to {companyName}",
"greeting": "Hello {userName},",
"content": "We're excited to have you on board...",
"ctaButton": "Get Started",
"footer": "Thanks for joining us!"
}
}
}
}Always include preview props for development testing:
CustomTemplate.PreviewProps = {
// Sample data for preview
} as CustomTemplatePropsExtend the notification types in your service types:
// In your notification types file
export const CustomNotificationTypes = {
CUSTOM_WELCOME: 'custom_welcome',
FEATURE_ANNOUNCEMENT: 'feature_announcement',
MAINTENANCE_ALERT: 'maintenance_alert',
USAGE_LIMIT_WARNING: 'usage_limit_warning',
} as const
// Add to existing NotificationType union
export type ExtendedNotificationType =
| NotificationType
| typeof CustomNotificationTypes[keyof typeof CustomNotificationTypes]Add a new email service function following the existing pattern:
// In src/services/email-service.ts
export const sendCustomWelcomeEmailService = async ({
email,
userName,
companyName,
dashboardUrl,
}: {
email: string
userName: string
companyName: string
dashboardUrl: string
}) => {
const t = await getTranslations('email.custom.welcome')
const fromEmail = env.EMAIL_FROM ?? 'onboarding@resend.dev'
await sendEmailService({
to: email,
subject: t('subject', { companyName }),
from: fromEmail,
text: t('preview'),
react: CustomWelcomeEmail({
userName,
companyName,
dashboardUrl,
}),
})
}Extend the notification service to handle your new types:
// Add to the notification service email handling
if (parsed.data.type === 'custom_welcome' && parsed.data.metadata) {
await sendCustomWelcomeEmailService({
email: targetUser.email,
userName: parsed.data.metadata.userName,
companyName: parsed.data.metadata.companyName,
dashboardUrl: parsed.data.metadata.dashboardUrl,
})
}If your new notification type should always send emails:
// Update the critical types array
const criticalTypes: NotificationType[] = [
// Existing critical types...
'maintenance_alert',
'security_breach_alert',
// Add your critical types here
]For more complex critical notification logic:
const isCriticalNotification = (type: NotificationType): boolean => {
const criticalTypes: NotificationType[] = [
// Standard critical types...
]
// Add custom logic
if (type === 'usage_limit_warning') {
// Could check user plan or usage level
return true // Or conditional logic
}
return criticalTypes.includes(type)
}Customize the Tailwind classes in your templates:
// Custom brand colors
<Text className="text-2xl font-bold text-brand-primary">
<Link className="text-brand-secondary hover:text-brand-secondary-dark">
// Custom fonts
<Body className="mx-auto my-auto bg-white px-2 font-brand">
// Custom spacing and layout
<Container className="mx-auto my-[60px] max-w-[600px] rounded-lg border border-brand-border p-12">Create template variants for different use cases:
// notification-email-variants.tsx
export function NotificationEmailInfo(props: NotificationProps) {
// Info variant with blue theme
}
export function NotificationEmailWarning(props: NotificationProps) {
// Warning variant with orange theme
}
export function NotificationEmailError(props: NotificationProps) {
// Error variant with red theme
}Create translation files for your new language:
messages/
├── en.json
├── fr.json
├── es.json
└── de.json # New language
Extend the language type definitions:
export type SupportedLanguage = 'fr' | 'en' | 'es' | 'de'Update your Next.js i18n configuration to include the new language
For templates requiring different layouts per language:
export default async function MultiLanguageTemplate({
content,
language,
}: {
content: string
language: 'fr' | 'en' | 'es' | 'de'
}) {
const t = await getTranslations({
locale: language,
namespace: 'email.multilingual'
})
// Language-specific layouts
if (language === 'ar') {
return <RightToLeftLayout content={content} translations={t} />
}
return <LeftToRightLayout content={content} translations={t} />
}Use the React Email CLI for template development:
# Install React Email CLI
npm install -g react-email
# Preview templates during development
cd src/lib/emails
react-email devCreate test functions for your custom templates:
// test-custom-templates.ts
export async function testCustomWelcomeEmail() {
try {
await sendCustomWelcomeEmailService({
email: 'test@example.com',
userName: 'Test User',
companyName: 'Test Company',
dashboardUrl: 'https://test.example.com/dashboard',
})
console.log('✅ Custom welcome email sent successfully')
} catch (error) {
console.error('❌ Error sending custom welcome email:', error)
}
}// Custom notification with business logic
export const sendUsageLimitWarningService = async (userId: string) => {
const user = await getUserByIdDao(userId)
const usage = await getUserUsageStats(userId)
const plan = await getUserPlan(userId)
if (usage.percentage > 80) {
await createTypedNotificationService({
userId,
type: CustomNotificationTypes.USAGE_LIMIT_WARNING,
metadata: {
currentUsage: usage.current,
limit: usage.limit,
percentage: usage.percentage,
planName: plan.name,
upgradeUrl: `${env.NEXT_PUBLIC_APP_URL}/billing/upgrade`,
},
})
}
}// Integrate with external services
export const sendSlackNotificationIntegration = async (
notificationData: CreateNotification
) => {
// Send email through existing system
await createTypedNotificationService(notificationData)
// Also send to Slack
if (notificationData.type === 'security_alert') {
await sendSlackAlert({
channel: '#security-alerts',
message: notificationData.message,
})
}
}System extended! You can now create custom email templates, add new notification types, and extend the system to match your specific business requirements.