Enviar Emails con Node.js en 2026: Nodemailer + Resend (Guía Completa)
Tutorial actualizado para enviar emails con Node.js usando Nodemailer con SMTP y Resend con su SDK. Includes HTML templates, adjuntos y gestión de errores.
Tabla de contenidos
Enviar emails con Node.js parece sencillo hasta que tus mensajes acaban en spam, el token de Gmail expira a las 24h o el SDK de turno tiene breaking changes sin documentar. Esta guía cubre las dos opciones que funcionan en 2026.
Opción 1: Resend (la opción moderna)
Resend es el servicio diseñado para developers. Tier gratuito generoso, SDK limpio y soporte para React Email.
Instalar
npm install resend
Envío básico
import { Resend } from 'resend';
const resend = new Resend(process.env.RESEND_API_KEY);
async function enviarBienvenida(email: string, nombre: string) {
const { data, error } = await resend.emails.send({
from: 'Fran <hola@tudominio.com>',
to: email,
subject: `Bienvenido, ${nombre}`,
html: `
<h1>Hola ${nombre} 👋</h1>
<p>Gracias por registrarte. Ya puedes acceder a tu cuenta.</p>
<a href="https://tuapp.com/dashboard">Ir al dashboard</a>
`,
});
if (error) {
console.error('Error enviando email:', error);
throw new Error(error.message);
}
return data;
}
Con React Email (templates reutilizables)
npm install @react-email/components
// emails/bienvenida.tsx
import { Html, Head, Body, Container, Heading, Text, Button } from '@react-email/components';
interface BienvenidaEmailProps {
nombre: string;
loginUrl: string;
}
export default function BienvenidaEmail({ nombre, loginUrl }: BienvenidaEmailProps) {
return (
<Html>
<Head />
<Body style={{ fontFamily: 'sans-serif', backgroundColor: '#f4f4f4' }}>
<Container style={{ maxWidth: '560px', margin: '0 auto', padding: '20px' }}>
<Heading>Hola, {nombre}</Heading>
<Text>Gracias por registrarte en nuestra plataforma.</Text>
<Button href={loginUrl} style={{ backgroundColor: '#6366f1', color: '#fff', padding: '12px 24px' }}>
Acceder a mi cuenta
</Button>
</Container>
</Body>
</Html>
);
}
// Usar el template con Resend
import { render } from '@react-email/render';
import BienvenidaEmail from './emails/bienvenida';
async function enviarConTemplate(email: string, nombre: string) {
const html = await render(BienvenidaEmail({ nombre, loginUrl: 'https://tuapp.com/login' }));
return resend.emails.send({
from: 'Fran <hola@tudominio.com>',
to: email,
subject: `Bienvenido, ${nombre}`,
html,
});
}
Opción 2: Nodemailer con SMTP
Nodemailer sigue siendo la opción estándar cuando necesitas SMTP directo (Gmail corporativo, Outlook 365, servidor propio).
npm install nodemailer
npm install -D @types/nodemailer
Configurar el transporter
import nodemailer from 'nodemailer';
// Para Gmail (requiere App Password, no la contraseña normal)
const transporter = nodemailer.createTransport({
service: 'gmail',
auth: {
user: process.env.GMAIL_USER,
pass: process.env.GMAIL_APP_PASSWORD, // App Password de Google, no tu contraseña
},
});
// Para servidor SMTP genérico (Brevo, Mailgun, etc.)
const transporter = nodemailer.createTransport({
host: process.env.SMTP_HOST, // smtp.brevo.com
port: Number(process.env.SMTP_PORT), // 587
secure: false, // true para 465, false para otros
auth: {
user: process.env.SMTP_USER,
pass: process.env.SMTP_PASS,
},
});
Enviar email
async function enviarEmail(to: string, subject: string, html: string) {
const info = await transporter.sendMail({
from: '"Mi App" <hola@tudominio.com>',
to,
subject,
html,
// Adjunto
attachments: [
{
filename: 'factura.pdf',
path: './facturas/factura-001.pdf',
contentType: 'application/pdf',
},
],
});
console.log('Email enviado:', info.messageId);
return info;
}
Configurar Gmail correctamente
Google desactivó el acceso con contraseña normal. Necesitas una App Password:
- Ve a tu cuenta Google → Seguridad → Verificación en dos pasos (actívala si no está)
- Busca “Contraseñas de aplicaciones”
- Genera una para “Correo” → “Otro”
- Esa cadena de 16 dígitos es
GMAIL_APP_PASSWORD
Probar emails en desarrollo: Mailtrap
Nunca envíes emails reales en desarrollo. Usa Mailtrap:
// .env.development
SMTP_HOST=sandbox.smtp.mailtrap.io
SMTP_PORT=2525
SMTP_USER=tu_usuario_mailtrap
SMTP_PASS=tu_contraseña_mailtrap
Todos los emails que envíes aparecen en el inbox de Mailtrap sin llegar a nadie.
Entregabilidad: por qué acaban en spam
La configuración DNS es tan importante como el código. Necesitas tres registros en tu dominio:
# SPF — declara qué servidores pueden enviar en tu nombre
TXT @ "v=spf1 include:_spf.resend.com ~all"
# DKIM — firma criptográfica de cada email
TXT resend._domainkey "v=DKIM1; k=rsa; p=MIGf..." (te lo da Resend/SendGrid)
# DMARC — política para emails que fallan SPF o DKIM
TXT _dmarc "v=DMARC1; p=none; rua=mailto:dmarc@tudominio.com"
Si usas variables de entorno correctamente en Node.js, las claves de Resend/SMTP nunca llegarán al repositorio.
Para integrar el envío de emails con la autenticación, mira la guía de Auth.js con Next.js — el flujo de “olvide mi contraseña” usa exactamente estos patrones.