| name | email-template |
| description | Responsive transactional email templates using React Email and Resend. Covers Scientific Luxury dark-theme email design, Australian locale formatting, template composition, preview tooling, and integration with the project's cron jobs and notification flows. |
| license | MIT |
| metadata | {"author":"NodeJS-Starter-V1","version":"1.0.0","locale":"en-AU"} |
Email Template - Responsive Transactional Emails
Description
Provides responsive transactional email template patterns for NodeJS-Starter-V1 using React Email and Resend. Covers Scientific Luxury dark-theme email design, template composition with shared components, preview tooling, and integration with the project's cron jobs and notification flows using Australian locale formatting.
When to Apply
Positive Triggers
- Creating transactional email templates (welcome, password reset, alerts)
- Building notification emails for cron job reports or agent alerts
- Designing email layouts that match the Scientific Luxury design system
- Setting up email delivery infrastructure (Resend, SMTP, SendGrid)
- Adding email preview and testing tooling
- User mentions: "email", "template", "notification", "transactional", "welcome email"
Negative Triggers
- Building in-app toast notifications (use UI component patterns)
- Sending Slack or webhook notifications (use
webhook-handler patterns)
- Generating PDF reports (use
pdf-generator patterns)
- Handling email as CSV import data (use
csv-processor instead)
Core Directives
The Four Rules of Email Templates
- Table-based layout: Email clients do not support CSS Grid/Flexbox — use
<table> wrappers
- Inline styles: Most clients strip
<style> blocks — React Email handles this automatically
- Dark theme first: Scientific Luxury OLED aesthetic adapted for email (dark background, spectral accents)
- Preview before send: Every template must render in the React Email preview before deployment
Recommended Stack
| Tool | Purpose | Install |
|---|
@react-email/components | Component library for email templates | pnpm add @react-email/components |
react-email | Dev server for previewing templates | pnpm add -D react-email |
resend | Email delivery API (recommended) | pnpm add resend |
Alternative Providers
| Provider | When to Use |
|---|
| Resend | Recommended — React Email native, simple API |
| SendGrid | Enterprise — high volume, advanced analytics |
| AWS SES | Cost-effective — already on AWS infrastructure |
| SMTP | Self-hosted — full control, no vendor lock-in |
File Structure
apps/web/
├── emails/ # Email templates
│ ├── components/ # Shared email components
│ │ ├── email-header.tsx # Logo + brand header
│ │ ├── email-footer.tsx # Unsubscribe + address
│ │ └── email-button.tsx # CTA button
│ ├── welcome.tsx # Welcome email
│ ├── password-reset.tsx # Password reset
│ ├── daily-report.tsx # Daily agent report
│ └── alert-notification.tsx # System alert
├── lib/email/
│ ├── send.ts # Email sending utility
│ └── config.ts # Provider configuration
Template Patterns
Base Layout Component
import {
Body,
Container,
Head,
Html,
Preview,
Section,
} from '@react-email/components';
interface EmailLayoutProps {
preview: string;
children: React.ReactNode;
}
export function EmailLayout({ preview, children }: EmailLayoutProps) {
return (
<Html>
<Head />
<Preview>{preview}</Preview>
<Body style={body}>
<Container style={container}>
{children}
</Container>
</Body>
</Html>
);
}
const body = {
backgroundColor: '#050505',
fontFamily: "'Inter', 'SF Pro Display', Helvetica, Arial, sans-serif",
margin: '0',
padding: ,
};
container = {
: ,
: ,
: ,
: ,
: ,
: ,
};
Shared Components
import { Heading, Hr, Link, Text } from '@react-email/components';
export function EmailHeader({ title }: { title: string }) {
return (
<>
<Text style={brandLabel}>NODEJS-STARTER-V1</Text>
<Heading style={heading}>{title}</Heading>
<Hr style={divider} />
</>
);
}
export function EmailButton({ href, children }: { href: string; children: string }) {
return (
<Link href={href} style={button}>
{children}
</Link>
);
}
export function EmailFooter() {
return (
);
}
brandLabel = {
: ,
: ,
: ,
: ,
: ,
: ,
};
heading = {
: ,
: ,
: ,
: ,
: ,
};
divider = {
: ,
: ,
: ,
};
button = {
: ,
: ,
: ,
: ,
: ,
: ,
: ,
: ,
: ,
};
footer = {
: ,
: ,
: ,
};
Example: Daily Report Email
import { Text } from '@react-email/components';
import { EmailLayout } from './components/email-layout';
import { EmailHeader, EmailFooter } from './components/email-header';
interface DailyReportProps {
date: string;
total: number;
completed: number;
failed: number;
successRate: string;
}
export function DailyReportEmail({
date, total, completed, failed, successRate,
}: DailyReportProps) {
return (
<EmailLayout preview={`Agent Report — ${date}`}>
<EmailHeader title="Daily Agent Report" />
<Text style={metric}>
<span style={metricLabel}>DATE</span>
< />
{date}
TOTAL RUNS
{total}
SUCCESS RATE
{successRate}
{failed > 0 && (
FAILED
{failed}
)}
);
}
metric = { : };
metricLabel = {
: ,
: ,
: ,
: ,
: ,
};
metricValue = {
: ,
: ,
: ,
: ,
};
Sending Emails
Resend Integration
import { Resend } from 'resend';
const resend = new Resend(process.env.RESEND_API_KEY);
interface SendEmailOptions {
to: string | string[];
subject: string;
react: React.ReactElement;
}
export async function sendEmail({ to, subject, react }: SendEmailOptions) {
const { data, error } = await resend.emails.send({
from: 'NodeJS-Starter-V1 <noreply@yourdomain.com.au>',
to,
subject,
react,
});
if (error) {
throw new Error(`Email send failed: ${error.message}`);
}
return data;
}
Cron Job Integration
Wire into the existing daily report cron:
import { sendEmail } from '@/lib/email/send';
import { DailyReportEmail } from '@/emails/daily-report';
if (process.env.REPORT_EMAIL_RECIPIENTS) {
const recipients = process.env.REPORT_EMAIL_RECIPIENTS.split(',');
await sendEmail({
to: recipients,
subject: `Agent Report — ${report.date}`,
react: DailyReportEmail({
date: new Date(report.date).toLocaleDateString('en-AU'),
total: report.summary.total,
completed: report.summary.completed,
failed: report.summary.failed,
successRate: report.summary.successRate,
}),
});
}
Scientific Luxury Email Palette
Adapted from the project's design tokens for email client compatibility:
| Token | Value | Usage |
|---|
| Background | #050505 | Email body |
| Container | #0a0a0a | Content area |
| Border | rgba(255, 255, 255, 0.06) | Dividers, container |
| Text primary | rgba(255, 255, 255, 0.9) | Headings, values |
| Text secondary | rgba(255, 255, 255, 0.7) | Body text |
| Text muted | rgba(255, 255, 255, 0.3) | Labels, footer |
| Cyan accent | #00F5FF | CTA buttons, links |
| Emerald | #00FF88 | Success metrics |
| Red | #FF4444 | Failure metrics |
| Amber | #FFB800 | Warning states |
Email Client Dark Mode Considerations
Some email clients force light mode. Add fallback:
const container = {
backgroundColor: '#0a0a0a',
border: '1px solid #1a1a1a',
};
Preview and Testing
React Email Dev Server
pnpm email:dev
Programmatic Rendering (For Tests)
import { render } from '@react-email/render';
import { DailyReportEmail } from '@/emails/daily-report';
const html = await render(
DailyReportEmail({
date: '23/01/2026',
total: 42,
completed: 38,
failed: 4,
successRate: '90.5%',
})
);
Anti-Patterns
| Anti-Pattern | Why It Fails | Correct Approach |
|---|
| CSS Grid/Flexbox in emails | Not supported in Outlook, Gmail | Table-based layout via React Email |
External <style> blocks | Stripped by most clients | Inline styles (React Email default) |
| Light theme email from dark app | Inconsistent brand experience | Dark theme email matching Scientific Luxury |
<img> without alt text | Accessibility failure | Always include descriptive alt |
| Hardcoded recipient addresses | Breaks across environments | Use REPORT_EMAIL_RECIPIENTS env var |
| Sending without preview | Rendering bugs in production | Always preview in React Email dev server |
Checklist for New Email Templates
Design
Implementation
Delivery
Australian Locale
Response Format
[AGENT_ACTIVATED]: Email Template
[PHASE]: {Design | Implementation | Review}
[STATUS]: {in_progress | complete}
{email template analysis or implementation guidance}
[NEXT_ACTION]: {what to do next}
Integration Points
Scientific Luxury
- Email palette derived from
apps/web/lib/design-tokens.ts
- Dark background, spectral accents, sharp corners, monospace data values
- No standard Bootstrap/Tailwind email themes
Cron Scheduler
- Daily report email wired into
/api/cron/daily-report route
- Alert emails triggered by cron health check failures
cron-scheduler patterns for scheduling digest emails
Structured Logging
- Log
email_sent, email_failed events with recipient count and template name
- Include
correlation_id for tracing email delivery through the pipeline
Error Taxonomy
- Email delivery failures:
SYS_EXTERNAL_EMAIL_PROVIDER (503)
- Invalid recipient:
DATA_VALIDATION_INVALID_EMAIL (422)
- Template rendering errors:
SYS_RUNTIME_EMAIL_RENDER (500)
Australian Localisation (en-AU)
- Date Format: DD/MM/YYYY in email body
- Currency: AUD ($) — formatted as
$X,XXX.XX
- Timezone: AEST/AEDT — timestamps display Australian time
- Spelling: colour, behaviour, analyse, organise, centre
- Footer: Australian business address (Brisbane, QLD)