Resend vs SendGrid vs Postmark
TL;DR
Resend for new projects — developer DX is best, React Email integration is native, generous free tier. Postmark for transactional email when deliverability is non-negotiable — they've maintained industry-leading inbox placement rates for 15 years. SendGrid for high-volume (1M+ sends) where cost matters and you're okay with a more complex API. In 2026, Resend has largely won the developer mindshare but Postmark's reputation still matters for apps where "password reset went to spam" is catastrophic.
Key Takeaways
- Resend: $0 free (3K/month), best DX, React Email native, $20/month (50K sends)
- Postmark: $15/month (10K sends), #1 deliverability, best for transactional-only use cases
- SendGrid: $20/month (100K sends), most features, complex API, used by large enterprises
- Deliverability: Postmark > Resend ≈ SendGrid for inbox placement rates
- React Email: all three support HTML/text, but Resend's SDK has first-class React Email integration
- Volume pricing: SendGrid cheapest at >100K sends/month; Resend most expensive
Resend: Developer-First Email
Best for: indie developers, SaaS builders, anyone using React for email templates
// npm install resend
import { Resend } from 'resend';
const resend = new Resend(process.env.RESEND_API_KEY);
// Send a plain HTML email:
const { data, error } = await resend.emails.send({
from: 'Acme <noreply@acme.com>',
to: ['user@example.com'],
subject: 'Welcome to Acme!',
html: '<h1>Welcome!</h1><p>Thanks for signing up.</p>',
text: 'Welcome! Thanks for signing up.', // Plain text fallback
});
React Email Integration (Killer Feature)
// npm install @react-email/components react react-dom
// emails/welcome.tsx:
import {
Html,
Head,
Preview,
Body,
Container,
Heading,
Text,
Button,
Hr,
} from '@react-email/components';
interface WelcomeEmailProps {
userName: string;
loginUrl: string;
}
export function WelcomeEmail({ userName, loginUrl }: WelcomeEmailProps) {
return (
<Html>
<Head />
<Preview>Welcome to our platform, {userName}!</Preview>
<Body style={{ fontFamily: 'Arial, sans-serif', backgroundColor: '#f4f4f4' }}>
<Container style={{ maxWidth: '600px', margin: '0 auto', padding: '20px' }}>
<Heading>Welcome, {userName}! 👋</Heading>
<Text>Thanks for signing up. Here's how to get started:</Text>
<Button
href={loginUrl}
style={{
backgroundColor: '#000',
color: '#fff',
padding: '12px 24px',
borderRadius: '4px',
textDecoration: 'none',
}}
>
Get Started →
</Button>
<Hr />
<Text style={{ color: '#666', fontSize: '12px' }}>
You received this because you signed up at acme.com.
</Text>
</Container>
</Body>
</Html>
);
}
// Send React Email with Resend:
import { render } from '@react-email/render';
import { WelcomeEmail } from '@/emails/welcome';
const emailHtml = render(WelcomeEmail({ userName: 'Alice', loginUrl: 'https://acme.com/login' }));
await resend.emails.send({
from: 'Acme <noreply@acme.com>',
to: [user.email],
subject: 'Welcome to Acme!',
html: emailHtml,
});
// Resend batch sending:
const { data } = await resend.batch.send([
{
from: 'noreply@acme.com',
to: 'user1@example.com',
subject: 'Your weekly report',
html: render(WeeklyReport({ user: user1 })),
},
{
from: 'noreply@acme.com',
to: 'user2@example.com',
subject: 'Your weekly report',
html: render(WeeklyReport({ user: user2 })),
},
]);
Resend Pricing
Free: 3,000 emails/month, 1 domain
Pro ($20/month): 50,000 emails/month, unlimited domains
Scale ($90/month): 300,000 emails/month
Beyond plan limits (Pro overage):
$1.50/1,000 emails = $0.0015/email
vs Postmark: $0.001/email
vs SendGrid: $0.0008/email at 100K
Postmark: Deliverability Champion
Best for: password resets, payment receipts, security alerts — any email that MUST reach the inbox
// npm install postmark
import * as postmark from 'postmark';
const client = new postmark.ServerClient(process.env.POSTMARK_API_TOKEN!);
// Send transactional email:
const response = await client.sendEmail({
From: 'noreply@acme.com',
To: 'user@example.com',
Subject: 'Reset your password',
HtmlBody: '<h1>Reset Your Password</h1><p>Click <a href="{{url}}">here</a>.</p>',
TextBody: 'Reset your password: {{url}}',
ReplyTo: 'support@acme.com',
MessageStream: 'outbound', // Or a named stream (e.g., 'password-resets')
});
console.log('Message ID:', response.MessageID);
// Postmark with templates (stored in dashboard):
await client.sendEmailWithTemplate({
From: 'noreply@acme.com',
To: user.email,
TemplateAlias: 'welcome', // Template created in Postmark dashboard
TemplateModel: {
product_name: 'Acme',
name: user.name,
action_url: `https://acme.com/verify?token=${token}`,
support_email: 'support@acme.com',
},
});
Message Streams
Postmark's key architectural feature — separate streams for transactional vs broadcast:
// Separate streams prevent marketing blocks from affecting transactional delivery:
// Transactional stream (highest priority, no unsubscribe required):
await client.sendEmail({
From: 'noreply@acme.com',
To: user.email,
Subject: 'Payment receipt',
HtmlBody: receiptHtml,
MessageStream: 'outbound', // Default transactional stream
});
// Broadcast stream (requires unsubscribe link):
await client.sendEmail({
From: 'newsletter@acme.com',
To: user.email,
Subject: 'Monthly newsletter',
HtmlBody: newsletterHtml,
MessageStream: 'newsletters', // Separate broadcast stream
});
Postmark Pricing
No free plan (100 test emails/month for devs)
$15/month: 10,000 emails
$45/month: 100,000 emails
$225/month: 1,000,000 emails
Per email at scale:
100K: $0.00045/email
1M: $0.000225/email
SendGrid: High-Volume Enterprise
Best for: 500K+ emails/month, marketing + transactional combined, existing SendGrid infrastructure
// npm install @sendgrid/mail
import sgMail from '@sendgrid/mail';
sgMail.setApiKey(process.env.SENDGRID_API_KEY!);
// Send email:
await sgMail.send({
to: 'user@example.com',
from: 'noreply@acme.com',
subject: 'Welcome!',
html: '<h1>Welcome!</h1>',
text: 'Welcome!',
});
// Dynamic templates (HTML + substitutions):
await sgMail.send({
to: user.email,
from: 'noreply@acme.com',
templateId: 'd-abc123', // Template ID from SendGrid
dynamicTemplateData: {
first_name: user.firstName,
reset_url: resetUrl,
},
});
// SendGrid Inbound Parse (receive emails via webhook):
// When a user replies to your email, SendGrid POSTs to your webhook:
export async function POST(request: Request) {
const formData = await request.formData();
const from = formData.get('from') as string;
const subject = formData.get('subject') as string;
const text = formData.get('text') as string;
// Handle reply email
await processReply({ from, subject, text });
return new Response('OK');
}
SendGrid Pricing
Free: 100 emails/day forever
Essentials ($20/month): 100K emails/month
Pro ($90/month): 1M emails/month
Premier: Custom
Per email at scale:
100K: $0.0002/email
1M: $0.00009/email
SendGrid is ~4x cheaper than Resend at 100K emails.
But has more moving parts (suppression groups, event webhooks, template engine).
Side-by-Side Comparison
| Resend | Postmark | SendGrid | |
|---|---|---|---|
| Free tier | 3K/month | 100/month | 100/day |
| Deliverability | Good | Best | Good |
| DX (API quality) | Best | Good | Complex |
| React Email native | ✅ | Partial | ❌ |
| Templates in dashboard | Basic | ✅ | ✅ |
| Marketing email | ❌ | Broadcasts | ✅ |
| Inbound email | ❌ | ✅ | ✅ |
| Webhooks | ✅ | ✅ | ✅ |
| 100K/month cost | ~$90 | ~$45 | ~$20 |
| Rate limit default | 2 req/s | None | None |
Recommendation
New SaaS (small to medium scale):
→ Use Resend
→ React Email templates, minimal config
→ Upgrade path exists (Scale plan for growth)
Established app where inbox placement is critical:
→ Use Postmark for transactional
→ Add SendGrid if you need marketing email
→ Message Streams = clean separation
High-volume or existing SendGrid users:
→ Stick with SendGrid
→ Cost savings significant at >500K emails/month
→ Complex API but worth it at scale
Compare all email APIs at APIScout.