Perfect for: Custom business logic, brand-specific emails, new notification types, and extending the system functionality.
Creating Custom Email Templates
Template Structure
Follow the existing pattern when creating new templates insrc/lib/emails/:
// Example: custom-welcome-email.tsx
import {
Body,
Container,
Head,
Html,
Link,
Preview,
Section,
Tailwind,
Text,
} from '@react-email/components'
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>
)
}Template Best Practices
1
Consistent Styling
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">2
Translation Integration
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!"
}
}
}
}3
Preview Props
Always include preview props for development testing:
CustomTemplate.PreviewProps = {
// Sample data for preview
} as CustomTemplatePropsAdding New Notification Types
Define New Notification Types
Extend 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]Create Email Service Function
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,
}),
})
}Integrate with Notification Service
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,
})
}Extending Critical Notifications
Update Critical Types List
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
]Custom Critical Logic
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)
}Styling Customization
Brand Colors and Fonts
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">Template Variants
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
}Multi-Language Support
Adding New Languages
1
Add Translation Files
Create translation files for your new language:
messages/
├── en.json
├── fr.json
├── es.json
└── de.json # New language
2
Update Language Types
Extend the language type definitions:
export type SupportedLanguage = 'fr' | 'en' | 'es' | 'de'3
Configure Next.js i18n
Update your Next.js i18n configuration to include the new language
Language-Specific Templates
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} />
}Testing and Development
Email Preview Development
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 devTesting Custom Templates
Create 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)
}
}Integration Examples
Custom Business Logic
// 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`,
},
})
}
}Integration with External Services
// 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.