Resend: Transactional Email Done Right in 2026
Production-ready Resend setup guide with real error handling, React Email templates, and battle-tested patterns for indie hackers.
TL;DR
Resend eliminates email infrastructure headaches with a developer-first API, React-based templating, and reliable delivery. This guide covers production patterns, common pitfalls, and the exact errors you'll debug.
Why Resend Wins for Indie Hackers
Transactional email has historically meant choosing between fragile SMTP servers, vendor lock-in, or complex infrastructure. Resend (verify pricing in official docs for current rates) solves this by:
The tradeoff? You're dependent on their uptime. For indie projects, this is usually the right bet.
Getting Started: Setup That Won't Bite You Later
Installation
```bash npm install resend react-email ```
You'll need both packages. resend is the API client, react-email provides the template components and preview server.
Environment Setup
```bash
.env.local
RESEND_API_KEY=re_xxx_your_key_here ```Critical: Never commit API keys. Use environment variables in all environments (development, staging, production). Verify the current API key format in [official docs](https://resend.com/docs/api-reference/introduction).
Production Pattern: Welcome Email with Error Handling
Here's a pattern that handles real failure modes:
```typescript // lib/email.ts import { Resend } from 'resend'; import WelcomeEmail from '@/emails/welcome';
const resend = new Resend(process.env.RESEND_API_KEY);
export async function sendWelcomeEmail({ email, name, }: { email: string; name: string; }) { try { const data = await resend.emails.send({ from: 'onboarding@yourdomain.com', to: email, subject: 'Welcome to StillNotAThing', react: <WelcomeEmail name={name} />, });
// Always check the response
if (data.error) {
console.error('[Resend] Send failed:', data.error);
// Log to Sentry, DataDog, etc.
throw new Error(Email delivery failed: ${data.error.message});
}
console.log('[Resend] Email sent:', data.data?.id); return { success: true, id: data.data?.id }; } catch (error) { if (error instanceof Error) { console.error('[Resend] Exception:', error.message); } throw error; } } ```
Common Console Errors You'll Hit
Error 1: Missing API Key
```
Error: RESEND_API_KEY is not defined
at new Resend (index.js:1:45)
```
Fix: Verify .env.local exists and your environment loads it before the function runs.
Error 2: Invalid Domain
```
Error: Email not sent. Invalid from address. Please add and verify your domain.
at Object.<anonymous> (resend.ts:15:19)
```
Fix: You must verify domain ownership in Resend dashboard before sending from custom domains. Use onboarding@resend.dev for testing.
Error 3: Rate Limiting (Production) ``` Error: Too many requests. Please wait before sending more emails. ``` Fix: Implement exponential backoff with a queue. See [implementing reliable queues](/?guide=queue-patterns) for production safety.
React Email Templates: Write JSX, Not HTML
This is Resend's killer feature. Your template is a React component:
```typescript // emails/welcome.tsx import { Body, Button, Container, Head, Hr, Html, Img, Link, Preview, Row, Section, Text, } from 'react-email';
interface WelcomeEmailProps { name: string; }
export default function WelcomeEmail({ name }: WelcomeEmailProps) { return ( <Html> <Head /> <Preview>Welcome to StillNotAThing</Preview> <Body style={main}> <Container style={container}> <Section style={box}> <Text style={heading}>Welcome, {name}! π</Text> <Hr style={hr} /> <Text style={paragraph}> You've joined a community of indie hackers shipping real products. </Text> <Button pX={20} pY={12} style={button} href="https://yourdomain.com/dashboard" > Start Building </Button> </Section> <Text style={footer}> Questions? Reply to this email. </Text> </Container> </Body> </Html> ); }
const main = { backgroundColor: '#ffffff', fontFamily: '-apple-system,BlinkMacSystemFont,"Segoe UI","Roboto","Oxygen","Ubuntu","Cantarell","Fira Sans","Droid Sans","Helvetica Neue",sans-serif', };
const container = { border: '1px solid #eaeaea', borderRadius: '5px', margin: '40px auto', padding: '20px', width: '465px', };
const box = { padding: '0 0 20px', };
const hr = { borderColor: '#dddddd', marginTop: '20px', marginBottom: '20px', };
const paragraph = { color: '#525f7f', fontSize: '16px', lineHeight: '1.5', textAlign: 'left' as const, };
const heading = { fontSize: '24px', fontWeight: 'bold', margin: '16px 0', };
const button = { backgroundColor: '#000', borderRadius: '4px', color: '#fff', fontSize: '16px', fontWeight: 'bold', textDecoration: 'none', textAlign: 'center' as const, display: 'block', };
const footer = { color: '#999999', fontSize: '12px', lineHeight: '1.5', }; ```
This gives you type safety, reusability, and templates that live in version control.
Testing Emails Locally
```bash
Start the React Email preview server
npx react-email preview ```Visit localhost:3000 to see all templates rendered. This catches rendering bugs before production.
Verification Checklist Before Shipping
resend.emails.send() callsWhat am I missing?
Resend's feature set evolves constantly. Readers: did we miss critical error patterns you've hit? Better queue implementations? Advanced templating tricks? Drop corrections and additions in the commentsβthis guide improves with your input.