dist/plugins/api-email-resend-react-email/skills/api-email-resend-react-email/SKILL.md
Resend + React Email templates
npx skillsauth add agents-inc/skills api-email-resend-react-emailInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
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>
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:
When NOT to use:
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:
When to send emails:
When NOT to send emails:
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.
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.
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.
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.
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.
// 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.
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.
</patterns>Detailed Resources:
<red_flags>
High Priority Issues:
render() - sends "[object Promise]" as email bodyMedium Priority Issues:
Common Mistakes:
Grid or Flexbox in email templates (not supported by email clients)rem units (email clients handle differently)PreviewProps for dev serverGotchas & Edge Cases:
react prop directly (renders internally), but pre-rendering with await render() + html gives you control over the output and works outside the Resend SDKrender() is async in React Email 5.0+ (renderAsync deprecated)attachments or scheduledAtwebhookSecret parameter (not secret)id, timestamp, signatureresend.emails.send(), not in the email payload headers@react-email/tailwind wrapper (Tailwind 4 supported in React Email 5.0+)</red_flags>
<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>
development
Material Design component library for Vue 3
development
VitePress 1.x — Vue-powered static site generator for documentation sites, built on Vite
tools
Docusaurus 3.x documentation framework — site configuration, docs/blog plugins, sidebars, versioning, MDX, swizzling, and deployment
development
TanStack Form patterns - useForm, form.Field, validators, arrays, linked fields, createFormHook, type safety