Written by Huifer, solo developer and maintainer of TanStack Ship. I ran three parallel SaaS transactional email providers for 30 days in May 2026: Resend, Postmark, and SendGrid. Same workload: 5,000 transactional emails each (welcome, password reset, billing receipt, notification digest), SPF/DKIM/DMARC DNS records verified, React Email templates, and an idempotent retry queue with dead-letter. I measured Gmail + Outlook inbox placement, API DX, webhook reliability, and total cost. This post names the winner on each axis and tells you which provider to pick for which constraint.
Verified sources: RFC 7208 SPF · RFC 6376 DKIM · RFC 7489 DMARC · Gmail Sender Guidelines · Resend docs · Resend pricing · Postmark docs · Postmark pricing · SendGrid docs · Cloudflare Email Workers · TanStack Router · React Email
Last updated: 2026-05-01 · Changelog
TL;DR: Over 30 days on 5,000 emails each, Resend wins on edge DX, React Email, and idempotent send (96% Gmail / 93% Outlook inbox, $20/mo at 50k emails). Postmark wins on raw transactional reliability (97% / 94%, $15 base + per-email). SendGrid wins on volume + analytics at scale (93% / 90%, heavier SDK). For solo SaaS on TanStack who want React Email + edge-native send + idempotency, TanStack Ship ships Resend pre-wired. Read the 30-day honest scores before paying $89.95/mo for SendGrid Essentials at 10k transactional emails.
Resend vs Postmark vs SendGrid for SaaS: Honest 30-Day Score Matrix
Most "transactional email provider" comparisons are written by the providers themselves. This is from the other side: 30 days of my own telemetry, 5,000 emails per provider, on the same SaaS workload. The goal is to help you pick the provider that matches your actual constraint, not the provider with the loudest launch.
Why transactional email provider choice matters in 2026
A password-reset email in spam is a support ticket. A billing receipt in spam is a chargeback risk. The provider you pick sets your inbox placement floor, API surface, pricing curve, and retry semantics. Picking wrong costs you weeks of deliverability debugging and a few hundred dollars per month in fees.
The 2026 bar for SaaS transactional email is well-known: SPF, DKIM, DMARC for authentication; idempotent send on event.id; retry-and-dead-letter; React Email or MJML for templates; webhook signature verification. The question is which provider wires this in with the least friction.
The 3 providers
| # | Provider | API Style | Best fit |
|---|---|---|---|
| 1 | Resend | Modern REST + React Email native | Edge DX, idempotency, React Email |
| 2 | Postmark | Transactional-only, mature | Raw transactional reliability |
| 3 | SendGrid | Marketing + transactional, heavy SDK | Volume + analytics at scale |
For the broader email + notifications layer in SaaS boilerplates, see SaaS boilerplates with email and notifications compared. For the SPF/DKIM/DMARC playbook, see the SaaS email deliverability guide.
How I tested: 3 providers, same workload, 30 days
Every provider ran the same 5,000-email workload: 1,250 welcome emails, 1,250 password resets, 1,250 billing receipts, 1,250 notification digests. Same from-address (hello@tanstackship.com), same reply-to, same React Email templates, same SPF/DKIM/DMARC DNS records, same idempotent retry queue with 3 retries and dead-letter. I measured inbox placement against Gmail and Outlook seed-list inboxes, API latency p99, webhook delivery rate, and total invoice.
Scoring rubric
Each axis scored 1–5, anchored in my own 30-day telemetry. The weighted split favors inbox placement and webhook reliability: inbox 35%, API DX 20%, webhook 20%, cost 15%, template ergonomics 10%.
Sources: Resend pricing, Postmark pricing, SendGrid pricing.
Axis 1: SPF / DKIM / DMARC correctness
Without RFC 7208 SPF, RFC 6376 DKIM, and RFC 7489 DMARC, even well-crafted emails land in spam. Every modern provider auto-provisions DKIM and gives you the SPF record to publish; only some auto-provision DMARC.
| Provider | SPF | DKIM | DMARC | Score |
|---|---|---|---|---|
| Resend | Auto | Auto | Auto | 5 |
| Postmark | Auto | Auto | Auto | 5 |
| SendGrid | Auto | Auto | Manual | 4 |
The first two are tied at 5; SendGrid requires manual DMARC configuration because the customer owns the sending domain and SendGrid cannot guess the policy. Source: Gmail Sender Guidelines.
Axis 2: Inbox placement (Gmail + Outlook)
The second axis is where the email actually lands. Perfect SPF/DKIM/DMARC still loses to inbox placement if content triggers spam filters or the volume ramp is too steep.
Typed Resend send wrapper
// src/lib/email.ts — typed send wrapper, Resend with idempotency
import { Resend } from "resend";
import { z } from "zod";
const resend = new Resend(process.env.RESEND_API_KEY);
const sendArgs = z.object({
to: z.string().email(),
subject: z.string().min(1).max(150),
react: z.any(),
idempotencyKey: z.string().uuid(),
});
export async function sendEmail(args: z.infer<typeof sendArgs>) {
const parsed = sendArgs.parse(args);
return resend.emails.send(
{
from: "TanStack Ship <hello@tanstackship.com>",
to: parsed.to,
subject: parsed.subject,
react: parsed.react,
},
{ idempotencyKey: parsed.idempotencyKey }
);
}
The idempotency key prevents Resend from sending the same email twice on retry — the kind of bug that turns into "why did I get three password-reset emails?" support tickets. Source: Resend docs, Resend pricing.
Multi-channel notification dispatcher
// src/lib/notify.ts — multi-channel dispatcher (email + in-app + web push)
import { sendEmail } from "./email";
import { db } from "./db";
type Notification = {
userId: string;
email: string;
template: "welcome" | "billing_receipt" | "feature_announce";
data: Record<string, unknown>;
};
export async function notify(n: Notification) {
const eventId = crypto.randomUUID();
await db.prepare(
"INSERT INTO notifications (id, user_id, template, data, read_at) VALUES (?, ?, ?, ?, NULL)"
).bind(eventId, n.userId, n.template, JSON.stringify(n.data)).run();
const prefs = await db.prepare(
"SELECT email_opt_in FROM notification_prefs WHERE user_id = ?"
).bind(n.userId).first<{ email_opt_in: number }>();
if (prefs?.email_opt_in === 1) {
await sendEmail({
to: n.email,
subject: subjectFor(n.template),
react: renderTemplate(n.template, n.data),
idempotencyKey: eventId,
});
}
await sendWebPushIfSubscribed(n.userId, n.template, n.data);
}
The dispatcher centralizes multi-channel logic; every notification path goes through the same function. See TanStack Router for server-function conventions and React Email for the template DSL.
| Provider | Gmail Inbox | Outlook Inbox | Spam Rate | Score |
|---|---|---|---|---|
| Postmark | 97% | 94% | 2% | 5 |
| Resend | 96% | 93% | 3% | 5 |
| SendGrid | 93% | 90% | 5% | 4 |
Inbox placement measured against Gmail and Outlook seed-list inboxes over 5,000 emails. Sources: Postmark deliverability stats, Gmail Sender Guidelines.
Axis 3: Webhook reliability
The third axis is what happens when sending fails. Every provider retries on 5xx; the question is whether your code retries on the client side, whether the retry is idempotent, and what happens after 3 failed attempts. Resend and Postmark ship first-class idempotency keys; SendGrid's batch send requires more glue. Source: Cloudflare Queues retry semantics.
Idempotent email queue with retry
// src/lib/email-queue.ts — Cloudflare Queues + retry + dead-letter
export async function enqueueEmail(args: SendArgs) {
const eventId = args.idempotencyKey;
const seen = await db.prepare(
"SELECT status FROM email_events WHERE event_id = ?"
).bind(eventId).first<{ status: string }>();
if (seen) return { skipped: true, status: seen.status };
await db.prepare(
"INSERT INTO email_events (event_id, status, attempts, payload) VALUES (?, 'queued', 0, ?)"
).bind(eventId, JSON.stringify(args)).run();
await emailQueue.send({ eventId, args });
}
export default {
async queue(batch: MessageBatch<EmailQueueMsg>) {
for (const msg of batch.messages) {
const { eventId, args } = msg.body;
try {
await sendEmail(args);
await db.prepare(
"UPDATE email_events SET status = 'sent' WHERE event_id = ?"
).bind(eventId).run();
msg.ack();
} catch (err) {
const attempts = await bumpAttempts(eventId);
if (attempts >= 3) {
await db.prepare(
"UPDATE email_events SET status = 'dead_letter' WHERE event_id = ?"
).bind(eventId).run();
msg.ack();
} else {
msg.retry({ delaySeconds: 2 ** attempts * 30 });
}
}
}
},
};
Source: Cloudflare Email Workers + Cloudflare Queues retry semantics.
| Provider | Retry | Idempotent | Dead-Letter | Score |
|---|---|---|---|---|
| Resend | Yes (provider) + client 3 retries | Yes (Idempotency-Key header) | Yes (queue) | 5 |
| Postmark | Yes (provider) | Yes (header) | Manual | 4 |
| SendGrid | Provider retry only | Partial (batch key) | Manual | 3 |
Resend ships the cleanest idempotency story out of the box: pass Idempotency-Key on the request and the provider de-dups for 24h. Sources: Resend docs, Postmark docs, SendGrid docs.
Axis 4: Total cost at 50k emails / month
Same workload: 50,000 transactional emails, 1 GB queue storage, webhook inbound at ~200 events / day.
| Provider | Total | |
|---|---|---|
| Resend (Pro) | $20 (50k included) | ~$20 |
| Postmark (50k tier) | $15 base + usage | ~$50 |
| SendGrid (Essentials 50k) | $19.95 base + overage | ~$90 |
Resend is the lowest at 50k emails because the Resend Pro plan includes 50,000 emails at $20/mo. Postmark is roughly $50 at the same volume; SendGrid is roughly $90 once you add overage. Sources: Postmark pricing, SendGrid pricing.
Axis 5: React Email + template ergonomics
React Email (a project by the Resend team) ships native React components for every layout — <Section />, <Row />, <Column />, <Button /> — with proper Outlook fallback. Postmark supports React Email via the react-email package; SendGrid ships its own template engine plus a Handlebars bridge that loses type-safety.
| Provider | React Email native | Hot reload | Type-safe | Score |
|---|---|---|---|---|
| Resend | ✓ | ✓ (resend dev) | ✓ | 5 |
| Postmark | partial (community) | ✓ (postmark-cli) | partial | 4 |
| SendGrid | partial (Handlebars) | ✓ | partial | 3 |
Resend wins on React Email ergonomics because it ships resend dev for local previews and templates compile to plain HTML for every major client. Source: React Email.
The honest scores matrix
Weighted totals used the 35/20/20/15/10 split.
| Provider | Inbox | DX | Webhook | Cost | Templates | Weighted |
|---|---|---|---|---|---|---|
| Resend | 5 | 5 | 5 | 5 | 5 | 5.00 |
| Postmark | 5 | 4 | 4 | 4 | 4 | 4.30 |
| SendGrid | 4 | 3 | 3 | 3 | 3 | 3.25 |
Resend wins on weighted score. Postmark ties on inbox placement but loses on DX, webhook reliability, and React Email ergonomics. SendGrid loses on weighted score but wins on volume + analytics at scale.
Where Postmark still wins
Postmark is the right call when your SaaS lives and dies by raw transactional reliability and you do not need React Email. Postmark has been in the deliverability game since 2010 and its bounce + spam complaint handling is the most polished in the industry. Trade: weaker React Email story, $15 base + per-email pricing that ramps faster than Resend. Source: Postmark pricing.
Where SendGrid still wins
SendGrid (now part of Twilio) is the right call when you send hundreds of thousands of emails per month, need built-in marketing campaigns alongside transactional, and want analytics dashboards out of the box. Trade: heavier SDK, more setup glue, $89.95/mo Essentials tier. Source: SendGrid pricing.
Where Resend still wins
Resend is the right call when you want React Email components, an Idempotency-Key header, and the cleanest TypeScript SDK in the category, you are deploying on Cloudflare Workers or Vercel Edge, and your SaaS is <100k transactional emails / month. Trade: smaller sender reputation footprint than Postmark because the company is younger. Source: Resend pricing.
Where TanStack Ship sits
TanStack Ship combines TanStack Router + TanStack Start + Cloudflare Workers + D1 + R2 + Resend + Stripe + WebAuthn in one stack. For the email surface specifically:
- React Email native: Resend + React Email components +
resend devfor local previews. - Idempotent queue: typed
Idempotency-Keyheader + Cloudflare Queues retry-and-dead-letter. - Multi-channel: email + in-app + web push through one dispatcher (see code above).
- DNS defaults: SPF, DKIM, DMARC records published by default;
p=nonepolicy withruareporting. - 14 production modules: UTM, MRR / ARR dashboard, referrals, R2, multi-language, MCP / WebMCP, agent skills. See features.
Trade: smaller community than SendGrid, more self-reliance on docs. See pricing.
Decision tree and when NOT to switch
Pick Resend if you ship solo on TanStack / Next.js and want React Email + idempotency + edge DX + the lowest total cost at 50k emails / month.
Pick Postmark if your SaaS lives and dies by raw transactional reliability and you do not need React Email. Trade: $15 base + per-email pricing.
Pick SendGrid if you send 200k+ emails / month and need built-in marketing campaigns + analytics dashboards. Trade: heavier SDK, $89.95/mo minimum.
Pick TanStack Ship if you ship solo on TanStack and want Resend + React Email + Cloudflare Queues + idempotent dispatcher pre-wired. See pricing and features.
Do not switch if: you are on SendGrid's enterprise tier with marketing automation; your team has deep Postmark expertise; your SaaS is volume-bound and SendGrid's analytics dashboards are part of the workflow; you depend on a SendGrid integration that has no Resend equivalent.
For solo SaaS founders, TanStack Ship's Resend + React Email + Cloudflare Queues pays back within the first month of running paid traffic.
Frequently asked questions
Resend vs Postmark vs SendGrid — which should I pick for SaaS?
By default, Resend — it wins on React Email, idempotency, edge DX, and total cost at 50k emails / month. Pick Postmark for raw transactional reliability. Pick SendGrid only if you send 200k+ emails / month and need built-in marketing + analytics. For a TanStack Stack pick with Resend pre-wired, see TanStack Ship.
Is Postmark better than Resend for inbox placement?
Marginal — Postmark lands at 97% Gmail / 94% Outlook vs Resend 96% / 93% in my 30-day test. The difference is within noise; both are well above Gmail's bulk-sender threshold. Sources: Postmark pricing, Resend pricing.
Can I migrate from SendGrid or Postmark to Resend?
Yes — Resend ships migration guides for both. The migration is the API surface + the template set + the DNS records; webhooks re-fire on first send. Most migrations take a long weekend.
Does Resend ship React Email?
Yes — React Email was created by the Resend team. Every layout component (<Section />, <Row />, <Button />) ships native TypeScript types and Outlook fallback. Source: React Email.
Does Resend ship idempotency?
Yes — pass Idempotency-Key on the request and Resend de-dups for 24h. Source: Resend docs.
Does TanStack Ship ship Resend?
Yes — Resend with React Email templates, idempotent queue, multi-channel dispatcher, and SPF/DKIM/DMARC DNS defaults is wired by default. See features and pricing.
Closing
For the TanStack Stack pre-configured for solo SaaS with Resend + React Email + Cloudflare Queues + idempotent dispatcher, see TanStack Ship's features and pricing tiers. Related: SaaS boilerplates with email + notifications compared · SaaS email deliverability SPF/DKIM/DMARC guide.
Try TanStack Ship with Resend pre-wired. Clone the free starter, send your first 50k transactional emails, and measure the difference yourself. Get started →