Resend: Transactional Email Done Right in 2026
Production-ready guide to Resend's email API. Real patterns, common errors, and why indie hackers prefer it over alternatives.
TL;DR
Resend is a developer-first transactional email service built for modern apps. It handles SMTP complexity, provides React email templates, and scales reliably. Current version offers free tier with 100 emails/day. Setup takes 15 minutes; common errors involve domain verification and API key scope.
---
Why Transactional Email Matters
Every indie product needs reliable email: password resets, order confirmations, notifications. Most developers default to AWS SES (complex) or SendGrid (expensive). Resend fills the gap for teams wanting simplicity without compromise.
Transactional email differs from marketing email. You're not sending campaigns—you're sending critical, user-triggered messages. Deliverability, authentication, and speed matter intensely.
---
What Resend Gets Right
1. Developer Experience
Resend's API is genuinely minimal. No SMTP configuration. No XML parsing. Just JavaScript.
```javascript import { Resend } from 'resend';
const resend = new Resend(process.env.RESEND_API_KEY);
await resend.emails.send({ from: 'onboarding@example.com', to: 'user@gmail.com', subject: 'Welcome to our app', html: '<p>Your account is ready.</p>', }); ```
That's it. No class instantiation boilerplate. No callback hell. Async/await native.
2. React Email Templates
Resend maintains [React Email](https://react.email), an open-source library for building emails as React components. Version 0.0.x (verify in official docs for latest):
```javascript import { Button, Container, Text } from 'react-email'; import * as React from 'react';
const WelcomeEmail = ({ userName, resetLink }) => ( <Container> <Text>Hi {userName},</Text> <Text>Click below to reset your password:</Text> <Button href={resetLink}>Reset Password</Button> </Container> );
export default WelcomeEmail; ```
Then render and send:
```javascript import { render } from 'react-email'; import WelcomeEmail from './emails/welcome';
const html = render( <WelcomeEmail userName="Alice" resetLink="https://app.com/reset?token=xyz" /> );
await resend.emails.send({ from: 'noreply@example.com', to: 'alice@gmail.com', subject: 'Reset your password', react: <WelcomeEmail userName="Alice" resetLink={...} />, }); ```
No HTML hand-coding. Type-safe. Testable.
3. Built-in Authentication
Resend handles DKIM, SPF, and DMARC setup automatically. Domain verification is one-click in the dashboard (takes 5-10 minutes for DNS propagation).
4. Production Features
---
Common Errors You'll Hit
Error 1: Missing Domain Verification
```
ResendError: Invalid from address. Please add and verify the domain first.
```
Fix: Go to Resend dashboard → Domains → Add your domain. Add CNAME records DNS. Wait for propagation (not instant).
Error 2: API Key Scope Issues
``` UnauthorizedError: API key does not have permission to perform this action. ```
Fix: Regenerate your API key in the dashboard. Old keys sometimes persist in .env.local. Verify RESEND_API_KEY is loaded correctly:
```javascript if (!process.env.RESEND_API_KEY) { throw new Error('Missing RESEND_API_KEY environment variable'); } ```
Error 3: Invalid JSX in React Email
``` TypeError: Invalid use of JSX outside render function at <WelcomeEmail /> ```
Fix: Ensure you're using react prop, not html prop:
```javascript // ✗ Wrong await resend.emails.send({ from: 'test@example.com', to: 'user@gmail.com', html: <WelcomeEmail />, // JSX not valid here });
// ✓ Correct await resend.emails.send({ from: 'test@example.com', to: 'user@gmail.com', react: <WelcomeEmail />, // Proper prop }); ```
---
Production Pattern: Email Queue
Don't send email synchronously in request handlers. Queue it.
```javascript // app/api/signup.ts (Next.js example) import { Resend } from 'resend'; import { db } from '@/lib/db'; import { queue } from '@/lib/queue'; // Bull, RQ, or custom
const resend = new Resend(process.env.RESEND_API_KEY);
export async function POST(req) { const { email, name } = await req.json();
// 1. Create user in DB const user = await db.users.create({ email, name });
// 2. Queue email (returns immediately) await queue.add('send-welcome-email', { userId: user.id, email: user.email, name: user.name, });
// 3. Return success to client return Response.json({ success: true }); }
// workers/send-welcome-email.ts queue.process('send-welcome-email', async (job) => { const { userId, email, name } = job.data;
try { await resend.emails.send({ from: 'welcome@example.com', to: email, subject: 'Welcome!', react: <WelcomeEmail name={name} />, });
// Log success await db.emailLogs.create({ userId, type: 'welcome', status: 'sent', }); } catch (error) { console.error('Email send failed:', error); throw error; // Bull will retry } }); ```
Benefit: User gets instant response. Email sends asynchronously. Failures retry automatically.
---
Cost & Limits
Free tier (verify in official docs for current numbers):
Paid: Starts ~$20/month for higher volume. Per-email pricing drops with scale.
For indie products with <10k monthly active users, the free tier is generous.
---
Comparison: Why Resend Over Alternatives
| Service | Setup Time | API Simplicity | React Templates | Pricing | |---------|-----------|----------------|-----------------|----------| | Resend | 15 min | ⭐⭐⭐⭐⭐ | Yes | $0-20/mo | | SendGrid | 30 min | ⭐⭐⭐ | No | $20+/mo | | AWS SES | 60 min | ⭐⭐ | No | Pay-per-email | | Mailgun | 30 min | ⭐⭐⭐ | No | $35+/mo |
Resend wins on developer experience. Other services are stronger for high-volume campaigns (>100k/mo).
---
Next Steps
1. Create account: [resend.com](https://resend.com)
2. Add domain: Complete DNS verification
3. Install SDK: npm install resend (verify latest version in official docs)
4. Test send: Use the code above
5. Monitor deliverability: Check webhooks and bounce rates
See also: [authentication best practices](/?guide=api-security) and [scaling database queries](/?guide=database-optimization) for related patterns.
---
What am I missing?
Have you used Resend in production? Spotted issues with the domain setup flow? Built custom middleware around it? Comments below—especially if you've hit errors not covered here or found better patterns for batching sends.
---
References