1 min read
AutomatisierungEmail Automation mit Resend & React Email
Moderne Email Automation. React Email Templates, Resend API, Transactional Emails und Email Sequenzen.
Email AutomationResendReact EmailTransactional EmailEmail TemplatesNodemailer

Email Automation mit Resend & React Email
Meta-Description: Moderne Email Automation. React Email Templates, Resend API, Transactional Emails und Email Sequenzen.
Keywords: Email Automation, Resend, React Email, Transactional Email, Email Templates, Nodemailer, SMTP
Einführung
Email bleibt der wichtigste Kommunikationskanal. Mit React Email und Resend werden Email-Templates zu First-Class React Components – testbar, typsicher und versioniert.
Email Stack 2026
┌─────────────────────────────────────────────────────────────┐
│ MODERN EMAIL STACK │
├─────────────────────────────────────────────────────────────┤
│ │
│ Template Layer: │
│ ├── React Email (JSX → HTML) │
│ ├── MJML Alternative │
│ └── Tailwind Email Support │
│ │
│ Delivery Layer: │
│ ├── Resend (Developer-First) │
│ ├── SendGrid │
│ ├── Postmark │
│ └── AWS SES │
│ │
│ Features: │
│ ├── Transactional (Welcome, Reset, etc.) │
│ ├── Marketing (Newsletters) │
│ ├── Sequences (Drip Campaigns) │
│ └── Webhooks (Delivery Status) │
│ │
└─────────────────────────────────────────────────────────────┘React Email Setup
npm install @react-email/components react-email resend// emails/welcome.tsx
import {
Html,
Head,
Preview,
Body,
Container,
Section,
Text,
Button,
Img,
Hr,
Link
} from '@react-email/components';
interface WelcomeEmailProps {
userName: string;
verificationUrl: string;
}
export default function WelcomeEmail({
userName,
verificationUrl
}: WelcomeEmailProps) {
return (
<Html>
<Head />
<Preview>Willkommen bei unserer Plattform, {userName}!</Preview>
<Body style={main}>
<Container style={container}>
<Img
src="https://example.com/logo.png"
width="120"
height="40"
alt="Logo"
style={logo}
/>
<Section style={content}>
<Text style={heading}>Willkommen, {userName}! 👋</Text>
<Text style={paragraph}>
Vielen Dank für Ihre Registrierung. Bitte bestätigen Sie Ihre
E-Mail-Adresse, um alle Funktionen nutzen zu können.
</Text>
<Button style={button} href={verificationUrl}>
E-Mail bestätigen
</Button>
<Text style={paragraph}>
Oder kopieren Sie diesen Link in Ihren Browser:
</Text>
<Text style={link}>{verificationUrl}</Text>
</Section>
<Hr style={hr} />
<Section style={footer}>
<Text style={footerText}>
© 2026 Unser Unternehmen. Alle Rechte vorbehalten.
</Text>
<Link href="https://example.com/unsubscribe" style={footerLink}>
Abmelden
</Link>
</Section>
</Container>
</Body>
</Html>
);
}
// Styles
const main = {
backgroundColor: '#f6f9fc',
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif'
};
const container = {
backgroundColor: '#ffffff',
margin: '0 auto',
padding: '20px 0',
maxWidth: '600px'
};
const logo = {
margin: '0 auto 20px',
display: 'block'
};
const content = {
padding: '0 40px'
};
const heading = {
fontSize: '24px',
fontWeight: 'bold',
color: '#1a1a1a',
marginBottom: '20px'
};
const paragraph = {
fontSize: '16px',
lineHeight: '26px',
color: '#484848'
};
const button = {
backgroundColor: '#5046e5',
borderRadius: '8px',
color: '#ffffff',
fontSize: '16px',
fontWeight: 'bold',
textDecoration: 'none',
textAlign: 'center' as const,
display: 'block',
padding: '14px 24px',
margin: '20px 0'
};
const link = {
color: '#5046e5',
fontSize: '14px',
wordBreak: 'break-all' as const
};
const hr = {
borderColor: '#e6ebf1',
margin: '20px 0'
};
const footer = {
padding: '0 40px'
};
const footerText = {
fontSize: '12px',
color: '#9ca299'
};
const footerLink = {
color: '#9ca299',
fontSize: '12px'
};Resend Integration
// lib/email.ts
import { Resend } from 'resend';
import WelcomeEmail from '@/emails/welcome';
import PasswordResetEmail from '@/emails/password-reset';
import OrderConfirmationEmail from '@/emails/order-confirmation';
const resend = new Resend(process.env.RESEND_API_KEY);
// Single Email senden
export async function sendWelcomeEmail(
to: string,
userName: string,
verificationUrl: string
) {
const { data, error } = await resend.emails.send({
from: 'Unser Team <noreply@example.com>',
to,
subject: `Willkommen, ${userName}!`,
react: WelcomeEmail({ userName, verificationUrl })
});
if (error) {
console.error('Email send error:', error);
throw error;
}
return data;
}
// Password Reset
export async function sendPasswordResetEmail(
to: string,
resetUrl: string
) {
return resend.emails.send({
from: 'Sicherheit <security@example.com>',
to,
subject: 'Passwort zurücksetzen',
react: PasswordResetEmail({ resetUrl })
});
}
// Order Confirmation mit Anhang
export async function sendOrderConfirmation(
to: string,
order: Order,
invoicePdf: Buffer
) {
return resend.emails.send({
from: 'Shop <orders@example.com>',
to,
subject: `Bestellbestätigung #${order.id}`,
react: OrderConfirmationEmail({ order }),
attachments: [
{
filename: `Rechnung-${order.id}.pdf`,
content: invoicePdf
}
]
});
}
// Batch Send
export async function sendBulkEmails(
recipients: Array<{ email: string; data: any }>
) {
const emails = recipients.map(r => ({
from: 'Newsletter <news@example.com>',
to: r.email,
subject: 'Unser Newsletter',
react: NewsletterEmail(r.data)
}));
return resend.batch.send(emails);
}Email Sequenzen (Drip Campaigns)
import { Queue, Worker } from 'bullmq';
import IORedis from 'ioredis';
const connection = new IORedis(process.env.REDIS_URL!);
const emailQueue = new Queue('email-sequences', { connection });
// Sequence Definition
interface EmailSequence {
id: string;
name: string;
steps: Array<{
delay: number; // Millisekunden
template: string;
subject: string;
}>;
}
const onboardingSequence: EmailSequence = {
id: 'onboarding',
name: 'Onboarding Sequence',
steps: [
{
delay: 0,
template: 'welcome',
subject: 'Willkommen bei uns!'
},
{
delay: 24 * 60 * 60 * 1000, // 1 Tag
template: 'getting-started',
subject: 'Erste Schritte'
},
{
delay: 3 * 24 * 60 * 60 * 1000, // 3 Tage
template: 'tips',
subject: '5 Tipps für den Einstieg'
},
{
delay: 7 * 24 * 60 * 60 * 1000, // 7 Tage
template: 'feedback',
subject: 'Wie gefällt es Ihnen?'
}
]
};
// Sequence starten
async function startEmailSequence(
userId: string,
email: string,
sequenceId: string,
userData: Record<string, any>
) {
const sequence = getSequenceById(sequenceId);
if (!sequence) throw new Error(`Sequence ${sequenceId} not found`);
// Sequence State speichern
await db.emailSequenceState.create({
data: {
userId,
sequenceId,
currentStep: 0,
status: 'active'
}
});
// Jobs für jeden Step einplanen
for (let i = 0; i < sequence.steps.length; i++) {
const step = sequence.steps[i];
await emailQueue.add(
'send-sequence-email',
{
userId,
email,
sequenceId,
stepIndex: i,
template: step.template,
subject: step.subject,
userData
},
{
delay: step.delay,
jobId: `${userId}-${sequenceId}-${i}`
}
);
}
}
// Worker für Sequence Emails
const worker = new Worker('email-sequences', async (job) => {
const { userId, email, sequenceId, stepIndex, template, subject, userData } = job.data;
// Prüfen ob Sequence noch aktiv
const state = await db.emailSequenceState.findFirst({
where: { userId, sequenceId }
});
if (state?.status !== 'active') {
console.log(`Sequence ${sequenceId} for user ${userId} is not active, skipping`);
return;
}
// Email senden
const EmailComponent = getEmailTemplate(template);
await resend.emails.send({
from: 'Unser Team <team@example.com>',
to: email,
subject,
react: EmailComponent(userData)
});
// State updaten
await db.emailSequenceState.update({
where: { id: state.id },
data: { currentStep: stepIndex + 1 }
});
console.log(`Sent sequence email: ${template} to ${email}`);
}, { connection });
// Sequence abbrechen
async function cancelEmailSequence(userId: string, sequenceId: string) {
// State updaten
await db.emailSequenceState.updateMany({
where: { userId, sequenceId },
data: { status: 'cancelled' }
});
// Pending Jobs entfernen
const jobs = await emailQueue.getJobs(['delayed', 'waiting']);
for (const job of jobs) {
if (job.data.userId === userId && job.data.sequenceId === sequenceId) {
await job.remove();
}
}
}Email Preview & Testing
// email-preview.ts (Development)
import { render } from '@react-email/render';
import WelcomeEmail from '@/emails/welcome';
// HTML Preview generieren
const html = render(WelcomeEmail({
userName: 'Max Mustermann',
verificationUrl: 'https://example.com/verify?token=abc123'
}));
console.log(html);
// React Email Dev Server
// package.json
{
"scripts": {
"email:dev": "email dev --port 3001"
}
}
// Dann: npm run email:dev
// Browser: http://localhost:3001// Email Testing mit Jest
import { render } from '@react-email/render';
import WelcomeEmail from '@/emails/welcome';
describe('WelcomeEmail', () => {
it('renders with correct user name', () => {
const html = render(WelcomeEmail({
userName: 'Test User',
verificationUrl: 'https://example.com/verify'
}));
expect(html).toContain('Test User');
expect(html).toContain('Willkommen');
});
it('includes verification link', () => {
const html = render(WelcomeEmail({
userName: 'Test User',
verificationUrl: 'https://example.com/verify?token=123'
}));
expect(html).toContain('https://example.com/verify?token=123');
});
it('has valid HTML structure', () => {
const html = render(WelcomeEmail({
userName: 'Test User',
verificationUrl: 'https://example.com/verify'
}));
expect(html).toMatch(/<html/);
expect(html).toMatch(/<\/html>/);
});
});Webhook Handling (Delivery Status)
// app/api/webhooks/resend/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { Webhook } from 'svix';
const webhookSecret = process.env.RESEND_WEBHOOK_SECRET!;
export async function POST(request: NextRequest) {
const payload = await request.text();
const headers = {
'svix-id': request.headers.get('svix-id')!,
'svix-timestamp': request.headers.get('svix-timestamp')!,
'svix-signature': request.headers.get('svix-signature')!
};
const wh = new Webhook(webhookSecret);
let event: any;
try {
event = wh.verify(payload, headers);
} catch (err) {
return NextResponse.json({ error: 'Invalid signature' }, { status: 400 });
}
// Event Type handling
switch (event.type) {
case 'email.sent':
await handleEmailSent(event.data);
break;
case 'email.delivered':
await handleEmailDelivered(event.data);
break;
case 'email.opened':
await handleEmailOpened(event.data);
break;
case 'email.clicked':
await handleEmailClicked(event.data);
break;
case 'email.bounced':
await handleEmailBounced(event.data);
break;
case 'email.complained':
await handleEmailComplained(event.data);
break;
}
return NextResponse.json({ received: true });
}
async function handleEmailBounced(data: any) {
// Email-Adresse als ungültig markieren
await db.user.update({
where: { email: data.to },
data: { emailStatus: 'bounced' }
});
}
async function handleEmailComplained(data: any) {
// Spam-Beschwerde: User aus Listen entfernen
await db.user.update({
where: { email: data.to },
data: { marketingOptIn: false }
});
}Fazit
Moderne Email Automation braucht:
- React Email: Component-basierte Templates
- Resend: Developer-First API
- Sequences: Automatisierte Drip Campaigns
- Webhooks: Delivery Tracking
Email als Code – testbar, typsicher, versioniert.
Bildprompts
- "Email template components assembling like Lego blocks, React Email concept"
- "Email sequence timeline with automated sends, drip campaign visualization"
- "Delivery status tracking with checkmarks, email analytics dashboard"