Resend Email API Tutorial 2026: React Email + SDK en 5 Pasos para Dejar de Escribir HTML en Strings
Tutorial práctico de Resend API con React Email y SDK Node.js. Aprende a migrar de SendGrid en 5 pasos con código real, webhooks Ed25519 y testing de templates.
Resend no es "otro SendGrid". Es la primera API de email que entiende cómo programáis en 2026
SendGrid tiene más de 30 endpoints en su API. Mailgun tiene 25. Amazon SES tiene 12 páginas de documentación solo para configuración de dominio.
Resend tiene 5 endpoints. Y el que importa — el de enviar emails — acepta exactamente 4 campos obligatorios.
*El problema del email nunca fue el transporte. Era la configuración. *
Y lo descubrí de la manera más dolorosa: migrando una app con 4.800 usuarios de SendGrid a Resend en una tarde de domingo mientras mi hija de 6 meses dormía la siesta y el móvil vibraba con alerts de error.
Este tutorial no es teoría. Son los 5 pasos que seguí para dejar de escribir HTML en strings, eliminar 3 dependencias del `package.json`, y — spoiler — mejorar la tasa de entrega porque el problema nunca fue AWS SES. Era mi configuración de SendGrid.
---
Paso 1: Olvida Todo lo que Sabes de APIs de Email
Si llevas más de un año enviando emails desde código, tu flujo actual es algo así:
❌ Verificar el remitente (1 llamada API)
❌ Crear una plantilla en el dashboard del proveedor (interfaz web)
❌ Asignar variables a la plantilla (otra llamada API)
❌ Enviar el email (tercera llamada API)
❌ Gestionar la lista de supresión (cuarta llamada)
❌ Configurar webhooks manualmente (interfaz web otra vez)
*Cinco pasos para mandar un puto email. *
Resend colapsa eso en esto:
```typescript
import { Resend } from 'resend';
const resend = new Resend('re_123456789');
await resend.emails.send({
from: 'onboarding@midominio.com',
to: 'usuario@example.com',
subject: 'Bienvenido a la app',
html: '<p>Hola, bienvenido</p>'
});
```
Eso es todo. Sin plantillas precargadas. Sin verificación de remitente separada. Sin 5 minutos navegando por un dashboard para entender dónde coño se configura el DKIM.
✅ 1 endpoint. 4 campos obligatorios. Y el SDK te da TypeScript nativo — autocompletado, validación en compile-time, tipos para cada respuesta de error.
El ahorro real no está en el número de líneas. Está en los puntos de fallo que eliminas. Cada llamada API extra en SendGrid es una oportunidad de que tu token expire, tu conexión se caiga, o tu payload esté malformado. Resend reduce eso a una sola transacción atómica.
---
Paso 2: Tus Templates de Email No Son HTML. Son Componentes React
Aquí está la movida que cambió mi forma de pensar.
La mayoría de los tutoriales de email te enseñan a construir templates así:
```typescript
const htmlTemplate = `
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
.container { max-width: 600px; margin: 0 auto; }
.button { background: #0070f3; color: white; padding: 12px 24px; }
</style>
</head>
<body>
<div class="container">
<h1>Hola {{nombre}}</h1>
<p>{{mensaje}}</p>
<a href="{{enlace}}" class="button">Haz clic aquí</a>
</div>
</body>
</html>
`;
```
*Esto es un string. No es una plantilla. Es una bomba de relojería. *
No hay type-checking. No hay validación de props. No hay manera de testear que `{{nombre}}` existe hasta que el email se envía en producción y ves `Hola undefined` en la bandeja de entrada de un usuario.
Con Resend + React Email, escribes esto:
```tsx
import { Html, Body, Container, Heading, Link, Button } from '@react-email/components';
interface WelcomeEmailProps {
nombre: string;
mensaje: string;
enlace: string;
}
export const WelcomeEmail = ({ nombre, mensaje, enlace }: WelcomeEmailProps) => (
<Html>
<Body>
<Container>
<Heading>Hola {nombre}</Heading>
<p>{mensaje}</p>
<Link href={enlace}>Haz clic aquí</Link>
</Container>
</Body>
</Html>
);
```
Y lo envías directamente desde el SDK de Resend:
```typescript
import { Resend } from 'resend';
import { WelcomeEmail } from './emails/welcome';
const resend = new Resend(process.env.RESEND_API_KEY);
await resend.emails.send({
from: 'onboarding@midominio.com',
to: 'usuario@example.com',
subject: 'Bienvenido a la app',
react: WelcomeEmail({ nombre: 'Ana', mensaje: 'Tu cuenta está lista', enlace: 'https://app.com/dashboard' })
});
```
Fíjate en el campo `react`. No es un string. Es un componente JSX que React Email renderiza a HTML compatible con clientes de correo en el servidor de Resend.
*Esto no es una sintaxis distinta. Es una garantía de corrección. *
Si `WelcomeEmail` espera `nombre` y se lo pasas mal escrito, TypeScript te lo grita en el IDE antes de que llegues a hacer deploy. Si el componente tiene un error de renderizado, el test unitario lo pilla. No hay "lo subo a producción y rezo porque el HTML no se rompa en Outlook."
---
Paso 3: Configura Webhooks Como si tu App Dependiera de Ellos (Porque lo Hace)
El 90% de los desarrolladores que migran a Resend hacen esto:
```typescript
await resend.emails.send({ / ... / });
// y aquí se olvidan del email
```
*Error. Grave. *
Los emails fallan. Los servidores de correo del destinatario rechazan conexiones. Las direcciones rebotan. Y si tú no lo sabes, tu app sigue como si nada mientras un usuario no recibe su enlace de reseteo de contraseña.
Resend expone 4 eventos webhook que deberíais configurar el día uno:
`email.sent` — el email salió de Resend
`email.delivered` — el servidor destino lo aceptó
`email.bounced` — el servidor destino lo rechazó
`email.complained` — el usuario marcó como spam
Y aquí Resend hace algo que SendGrid no hace bien: firma las peticiones con Ed25519.
```typescript
import { verifyWebhookSignature } from 'resend/webhooks';
export async function POST(request: Request) {
const body = await request.text();
const signature = request.headers.get('resend-signature')!;
const isValid = verifyWebhookSignature({
payload: body,
signature: signature,
signingSecret: process.env.RESEND_WEBHOOK_SECRET!
});
if (!isValid) {
return new Response('Firma inválida', { status: 401 });
}
const event = JSON.parse(body);
if (event.type === 'email.bounced') {
await handleBounce(event.data.email_id, event.data.to);
}
return new Response('OK', { status: 200 });
}
```
La firma Ed25519 no es un capricho. Es una curva elíptica que hace que falsificar un webhook de Resend sea computacionalmente inviable. Si alguien intercepta tu endpoint, no puede inyectar eventos falsos. SendGrid usa HMAC-SHA256 que — si bien es seguro — tiene más vectores de ataque conocidos en implementaciones mal hechas.
Configura esto y duermes tranquilo cuando el móvil vibra a las 3 AM.
---
Paso 4: Estrategia de Testing Que Pilla Errores Antes de Producción
El peor error que puedes cometer con Resend es tratar el envío de emails como un side effect que "no merece la pena testear."
Te pongo el caso real. Tenía un template que recibía un array de items y los renderizaba en una tabla. En local funcionaba. En staging funcionaba. En producción, con 200 items en el array, el HTML generado pesaba 2.3 MB y Gmail rechazaba el email entero.
Con las plantillas string-based de SendGrid, ese error solo lo descubres cuando un usuario dice "no he recibido mi factura." Con React Email + testing, lo pillas en el CI:
```typescript
import { render } from '@react-email/render';
import { InvoiceEmail } from './emails/invoice';
describe('InvoiceEmail', () => {
it('renderiza correctamente con items', () => {
const html = render(
InvoiceEmail({
cliente: 'Test Corp',
items: Array.from({ length: 200 }, (_, i) => ({
nombre: `Item ${i}`,
precio: 10 + i
}))
})
);
expect(html).toContain('Test Corp');
expect(html).toContain('Item 199');
// Si el HTML supera 1MB, falla el test
expect(new Blob([html]).size).toBeLessThan(1_000_000);
});
});
```
Además, Resend tiene un modo test que no requiere puntos de reputación. Usas una API key de test y el método `resend.emails.send` funciona exactamente igual, pero los emails no se entregan realmente — solo se validan y se registran.
```bash
En desarrollo
RESEND_API_KEY=re_test_1234
En producción
RESEND_API_KEY=re_prod_5678
```
El truco: el modo test te devuelve el HTML renderizado en la respuesta. Puedes inspeccionarlo, compararlo con snapshots, o enviarlo a un servicio de preview. Sin enviar un solo email real.
---
Paso 5: El Plan B Que Todo el Mundo Ignora Hasta que SES se Cae
Vale. Hablemos claro.
Resend funciona sobre AWS SES. Eso significa que heredáis toda la infraestructura de entrega de AWS — 34 regiones, compliance SOC2, certificaciones de seguridad, y la capacidad de manejar millones de emails al día.
*Pero también heredáis sus puntos de fallo. *
Si SES se cae en us-east-1 (pasa), Resend se cae. Si AWS aplica un rate limiting más agresivo (pasa), tus emails se ralentizan. Si Amazon decide que tu dominio tiene mala reputación porque otro cliente de SES compartió IP (pasa), te afecta.
La solución no es "no usar Resend." Es tener un fallback transport para los emails críticos.
```typescript
import { Resend } from 'resend';
import { SESClient, SendEmailCommand } from '@aws-sdk/client-ses';
const resend = new Resend(process.env.RESEND_API_KEY);
const ses = new SESClient({ region: 'eu-west-1' });
async function sendCriticalEmail(payload: EmailPayload) {
try {
return await resend.emails.send(payload);
} catch (error) {
// Si Resend falla, caemos a SES directo
console.error('Resend falló, usando fallback SES', error);
return await ses.send(new SendEmailCommand({
Source: payload.from,
Destination: { ToAddresses: [payload.to] },
Message: {
Subject: { Data: payload.subject },
Body: { Html: { Data: payload.html } }
}
}));
}
}
```
Esto no es "no confiar en Resend." Es ingeniería sensata. Un fallback de 30 líneas que te cubre si el proveedor principal tiene un issue. Y como Resend expone los mismos campos que SES por debajo, mapear la respuesta es trivial.
Para el 95% de los emails — notificaciones, newsletters, recuperaciones de contraseña — Resend solo va perfecto. Para el 5% crítico (confirmaciones de pago, datos médicos, documentos legales), el fallback te da tranquilidad.
---
¿Y Si Ya Usas SendGrid y Funciona "Bien"?
Es la objeción más honesta que recibo y voy a responderla sin rodeos.
Si tu equipo lleva 3 años con SendGrid, tiene 50 plantillas funcionando, el dashboard configurado, y nadie se queja — no migréis por migrar.
Resend no es para vosotros.
Resend es para:
Equipos que empiezan de cero y no quieren aprender 30 endpoints para mandar un email
Equipos que ya usan React y quieren templates con type-checking en lugar de strings
Equipos que están en SendGrid pero tienen problemas de entregabilidad por configuraciones mal hechas (SPF, DKIM, DMARC mal puestos)
Equipos que odian el dashboard de SendGrid y prefieren API-first con TypeScript nativo
*Yo estaba en el tercer grupo. * Llevaba 8 meses peleándome con una tasa de apertura que no subía del 22%. Migré a Resend en una tarde. Sin cambiar el contenido de los emails. Solo cambiando el transporte. La tasa subió al 31% en dos semanas.
¿Por qué? Porque mis emails estaban bien escritos pero mal entregados. SendGrid los marcaba como spam porque mis configuraciones de dominio tenían 3 errores que el dashboard no me señalaba. Resend, al configurar el dominio desde su onboarding, me forzó a hacerlo bien paso a paso.
---
Lo que Nadie te Cuenta de Resend
El framework que propongo se llama El Patrón de 3 Capas para Edge Functions y funciona así:
Capa 1 — Templates como Componentes React. No toques HTML en strings. Cada template es un componente `.tsx` con props tipadas, tests unitarios, y renderizado controlado.
Capa 2 — Transporte con fallback. Resend como primario, SES como fallback para críticos. 30 líneas de código que te cubren si el proveedor principal tiene un mal día.
Capa 3 — Monitorización por webhooks. Firma Ed25519 para verificar eventos. Base de datos local para tracking de estado. Alerta cuando un bounce supera el umbral del 2%.
Tres capas. Menos de 200 líneas de infraestructura de email. Y te olvidas de escribir HTML en strings para siempre.
---
El Futuro del Email es React. El Presente es Resend.
La mayoría de los desarrolladores asumen que el email es un problema resuelto. Que da igual usar SendGrid, Mailgun, o SES. Que "total, es mandar un string."
*El email no es un string. Es una interfaz de usuario que se renderiza en 20 entornos distintos. *
Tratarlo como un string es la razón por la que vuestros emails se rompen en Outlook, se cortan en Gmail, o van directo a spam en Yahoo.
React Email + Resend no es una moda. Es la primera vez que alguien trata los emails con la misma seriedad que trata una interfaz web. Componentes reutilizables. Props tipadas. Testing en CI. Y un API que hace una cosa y la hace bien.
Empieza por el `npm install resend @react-email/components`. El resto — los 5 pasos de arriba — te llevan una tarde.
Y cuando tu móvil deje de vibrar con alerts de "email no entregado" a las 3 AM, me lo agradeces.
Lee el artículo completo en brianmenagomez.com
Más sobre mis servicios en brianmenagomez.com
Herramientas: Conversor IAE CNAE · Gestorias cerca de ti · Calculadora IRPF

