Email Patterns with Resend and React Email
SkillCommunicationThis skill gives your AI the ability to send email through Resend using templates built with React Email. Once added, your AI can compose email messages from reusable layouts and deliver them to recipients.
Available today. Use it from your connected AI after setup.
No other account needed.
Add the skill, then ask your AI to draft an email template or send a message and it will handle composing and delivery.
Then ask your AI: use the Email Patterns with Resend and React Email skill
What your AI can do with it
- Send email messages through Resend
- Build email layouts as React Email templates
- Compose emails from a reusable template
- Deliver formatted messages to recipients
What this skill tells your AI
The instructions your AI receives, as published by agents-inc/skills in src/skills/api-email-resend-react-email/SKILL.md and read by ahel’s review.
Quick Guide: Use Resend for transactional emails with React Email templates. Always
await render()before sending (it returns a Promise). Server-side only - never expose API keys to clients. Implement retry with exponential backoff for transient failures. Include unsubscribe links in non-transactional emails (CAN-SPAM). Useresend.batch.send()for 2-100 recipients (no attachments or scheduling support in batch). React Email 5.0+ deprecatedrenderAsync- userender()instead. Webhook verification requires raw request body andwebhookSecretparameter.
<critical_requirements>
CRITICAL: Before Using This Skill
All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering,
import type, named constants)
(You MUST await render() before passing HTML to resend.emails.send() - render returns a Promise)
(You MUST handle Resend API errors and implement retry logic for transient failures)
(You MUST use server-side sending for all emails - never expose RESEND_API_KEY to the client)
(You MUST include unsubscribe links in marketing/notification emails - required for CAN-SPAM compliance)
(You MUST use typed props interfaces for all email templates - enables compile-time validation)
</critical_requirements>
Auto-detection: Resend, React Email, @react-email/components, resend.emails.send, email template, transactional email, verification email, password reset email, notification email, email rendering, resend.batch.send, resend.webhooks.verify
When to use:
- Sending transactional emails (verification, password reset, receipts)
- Creating React Email templates with Tailwind styling
- Building notification systems with email delivery
- Implementing email tracking via webhooks
- Batch sending to multiple recipients
When NOT to use:
- Marketing campaign management (use dedicated marketing tools)
- SMS or push notifications (different services)
- Email list management (use Resend Audiences or marketing tools)
Philosophy
Email in modern applications follows a server-side, template-driven approach. React Email brings component patterns to email development, while Resend handles reliable delivery.
Core principles:
- Server-side only - Never expose API keys to clients
- Typed templates - Props interfaces catch errors at compile time
- Reliable delivery - Error handling with retry logic for transient failures
- Non-blocking - Fire-and-forget for non-critical emails
When to send emails:
- User authentication events (verification, password reset, 2FA)
- Transactional confirmations (purchases, signups, invitations)
- Important notifications (security alerts, account changes)
- Team collaboration (invites, mentions, updates)
When NOT to send emails:
- Every minor action (creates email fatigue)
- Marketing without consent (spam, illegal)
- Real-time alerts (use push notifications)
- In-app actions (show in-app notifications instead)
Core Patterns
Pattern 1: Email Template Structure
Define typed props, use React Email components, add PreviewProps for the dev server.
interface WelcomeEmailProps {
userName: string;
loginUrl: string;
features?: string[];
}
export function WelcomeEmail({ userName, loginUrl, features = [] }: WelcomeEmailProps) {
return (
<BaseLayout preview={`Welcome, ${userName}!`}>
<Heading>Welcome!</Heading>
<Text>Hi {userName},</Text>
<Button href={loginUrl}>Get Started</Button>
</BaseLayout>
);
}
WelcomeEmail.PreviewProps = { userName: "John", loginUrl: "..." } satisfies WelcomeEmailProps;
See examples/core.md Pattern 1 for complete template with layout and styling.
Pattern 2: Sending with Error Handling
Always await render(), check the { data, error } response, return typed results.
const html = await render(options.react); // CRITICAL: must await
const { data, error } = await resend.emails.send({
from: `${DEFAULT_FROM_NAME} <${DEFAULT_FROM_ADDRESS}>`,
to: options.to,
subject: options.subject,
html,
});
if (error) {
return { success: false, error: error.message };
}
return { success: true, id: data?.id };
See examples/core.md Pattern 2-3 for complete send wrapper and retry with exponential backoff.
Pattern 3: Async (Fire-and-Forget) Sending
For non-critical emails (welcome, notifications), don't block the response.
// Non-blocking - catches errors internally
sendEmailAsync(options);
return Response.json({ success: true }); // Returns immediately
Track in-flight promises for graceful shutdown. See examples/async-batch.md Pattern 1-2.
Pattern 4: Batch API
Use resend.batch.send() for 2-100 recipients. Render all templates in parallel.
const rendered = await Promise.all(
emails.map(async (e) => ({
from,
to: e.to,
subject: e.subject,
html: await render(e.react),
})),
);
const { data, error } = await resend.batch.send(rendered);
Batch limitations: No attachments, no scheduledAt. Tags and idempotency keys are supported. See examples/async-batch.md Pattern 3-4.
Pattern 5: Webhook Verification
Use resend.webhooks.verify() with the raw request body. JSON parsing breaks signature verification.
const payload = await request.text(); // Raw body, NOT .json()
const event = resend.webhooks.verify({
payload,
headers: {
id: request.headers.get("svix-id") ?? "",
timestamp: request.headers.get("svix-timestamp") ?? "",
signature: request.headers.get("svix-signature") ?? "",
},
webhookSecret: process.env.RESEND_WEBHOOK_SECRET!,
});
See examples/webhooks.md for full handler with event processing and Svix alternative.
Pattern 6: Scheduled Sending, Idempotency Keys, Tags
// Scheduled (up to 30 days, NOT supported in batch)
await resend.emails.send({ ...payload, scheduledAt: futureDate.toISOString() });
// Idempotency (256 char limit, expires 24h) — second argument to send()
await resend.emails.send(payload, {
idempotencyKey: `order-confirmation-${orderId}`,
});
// Tags (ASCII alphanumeric, underscores, dashes only)
await resend.emails.send({
...payload,
tags: [{ name: "campaign", value: "launch" }],
});
See examples/advanced-features.md for complete implementations with validation.
Pattern 7: Unsubscribe and Preferences
Non-transactional emails MUST include unsubscribe links (CAN-SPAM). Use signed tokens for security.
// In every notification/marketing template:
<Link href={unsubscribeUrl}>Unsubscribe from these notifications</Link>
// Generate signed unsubscribe URLs
const token = jwt.sign({ userId, category }, UNSUBSCRIBE_SECRET, { expiresIn: "30d" });
const url = `${APP_URL}/api/email/unsubscribe?token=${token}`;
See examples/preferences.md for preference schema, checking before send, and unsubscribe endpoint.
Detailed Resources:
- examples/core.md - Template structure, sending with error handling, retry logic
- examples/async-batch.md - Async sending, batch API
- examples/webhooks.md - Webhook handler with signature verification
- examples/templates.md - Password Reset, Notification templates
- examples/preferences.md - Unsubscribe, email preferences
- examples/advanced-features.md - Scheduled sending, idempotency keys, tags
- reference.md - Decision frameworks, anti-patterns
<red_flags>
RED FLAGS
High Priority Issues:
- Not awaiting
render()- sends"[object Promise]"as email body - API key exposed on client - security vulnerability
- No error handling - silent failures
- Missing unsubscribe links in non-transactional emails - CAN-SPAM violation
- Sending without checking user preferences - spam
Medium Priority Issues:
- No retry logic for transient failures (rate limits, 5xx errors)
- Sync sending blocking request handlers for non-critical emails
- Hardcoded from address instead of environment variable
- No webhook verification signature check
- Not logging email send results
Common Mistakes:
- Using
GridorFlexboxin email templates (not supported by email clients) - Expecting shadows or gradients to render in emails
- Using
remunits (email clients handle differently) - Forgetting
PreviewPropsfor dev server
Gotchas & Edge Cases:
- Resend SDK accepts a
reactprop directly (renders internally), but pre-rendering withawait render()+htmlgives you control over the output and works outside the Resend SDK render()is async in React Email 5.0+ (renderAsyncdeprecated)- Batch API limited to 100 emails, does NOT support
attachmentsorscheduledAt - Webhooks require raw request body - JSON parsing breaks signature verification
- Webhook verify uses
webhookSecretparameter (notsecret) - Webhook headers object uses short keys:
id,timestamp,signature - Idempotency keys are passed as a second argument to
resend.emails.send(), not in the email payload headers - Idempotency keys expire after 24 hours, max 256 characters
- Tags: ASCII alphanumeric, underscores, dashes only, max 256 chars per key/value
- Tailwind in emails requires
@react-email/tailwindwrapper (Tailwind 4 supported in React Email 5.0+) - Images must use absolute URLs (no relative paths)
</red_flags>
<critical_reminders>
CRITICAL REMINDERS
All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering,
import type, named constants)
(You MUST await render() before passing HTML to resend.emails.send() - render returns a Promise)
(You MUST handle Resend API errors and implement retry logic for transient failures)
(You MUST use server-side sending for all emails - never expose RESEND_API_KEY to the client)
(You MUST include unsubscribe links in marketing/notification emails - required for CAN-SPAM compliance)
(You MUST use typed props interfaces for all email templates - enables compile-time validation)
Failure to follow these rules will cause email delivery failures, security vulnerabilities, or legal compliance issues.
</critical_reminders>
Sources
Signals
- GitHub stars
- 24
- Forks
- 8
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
api-email-resend-react-email- Source
- github.com/agents-inc/skills