Framework guides
Next.js - Remix - Nuxt - Express - RedwoodJS - Hono - Bun - Astro
Node.js library for the Resend API.
npm install resend
# or
yarn add resend
Send email with:
First, you need to get an API key, which is available in the Resend Dashboard.
import { Resend } from 'resend';
const resend = new Resend('re_xxxx...xxxxxx');
Send your first email:
const { data } = await resend.emails.send({
from: '[email protected]',
to: '[email protected]',
replyTo: '[email protected]',
subject: 'hello world',
text: 'it works!',
});
console.log(`Email ${data.id} has been sent`);
[!NOTE]
In order to send from your own domain, you will first need to verify your domain in the Resend Dashboard.
Send an email custom HTML content:
const { data } = await resend.emails.send({
from: '[email protected]',
to: '[email protected]',
replyTo: '[email protected]',
subject: 'hello world',
html: '<strong>it works!</strong>',
});
console.log(`Email ${data.id} with custom HTML content has been sent.`);
Start by creating your email template as a React component.
import React from 'react';
export default function EmailTemplate({ firstName, product }) {
return (
<div>
<h1>Welcome, {firstName}!</h1>
<p>Thanks for trying {product}. We’re thrilled to have you on board.</p>
</div>
);
}
Then import the template component and pass it to the react property.
import EmailTemplate from '../components/EmailTemplate';
const { data } = await resend.emails.send({
from: '[email protected]',
to: '[email protected]',
replyTo: '[email protected]',
subject: 'hello world',
react: <EmailTemplate firstName="John" product="MyApp" />,
});
console.log(`Email ${data.id} with a React template has been sent`);
[!NOTE] If you're sending emails from a file that doesn't have JSX transpilation set up (e.g., in a
.js/.tsfile instead of JSX/TSX), use React'sjsxruntime function instead of passing the component as JSX:import { jsx } from 'react/jsx-runtime' import EmailTemplate from '../components/EmailTemplate'; await resend.emails.send({ from: '[email protected]', to: '[email protected]', replyTo: '[email protected]', subject: 'hello world', react: jsx(EmailTemplate, { firstName:"John", product:"MyApp" }), });
Every method takes request options as its last argument. Use signal to cancel a request or set a timeout, and headers to add request headers:
const controller = new AbortController();
await resend.emails.send({ ... }, { signal: controller.signal });
// With a timeout (Node.js 18+) and a custom header
await resend.emails.list(
{ limit: 50 },
{ signal: AbortSignal.timeout(5000), headers: { 'X-Trace-Id': traceId } },
);
An aborted request returns a network-failure error with a null status code. To tell a cancel apart from a real failure, check the status code and signal.aborted, and do not match on the error message:
const signal = AbortSignal.timeout(5000);
const { error } = await resend.emails.list({}, { signal });
if (error?.statusCode === null && signal.aborted) {
// The request was cancelled or timed out.
}
When a method has an optional argument before the request options, pass undefined in its place:
await resend.broadcasts.send(broadcastId, undefined, { signal });
A chained publish() sends a separate request, so it takes its own options:
await resend.templates
.create({ name: 'welcome', html: '<p>Hi</p>' }, { signal })
.publish({ signal });
Create an inbox and list its threads:
const { data: inbox } = await resend.inboxes.create({
emailAddress: '[email protected]',
name: 'support',
});
const { data: threads } = await resend.inboxes.threads.list({
inboxId: inbox.id,
folders: ['inbox'],
});
Every inbox method also accepts the inbox email address instead of its ID:
const { data: threads } = await resend.inboxes.threads.list({
emailAddress: '[email protected]',
folders: ['inbox'],
});
await resend.inboxes.update('[email protected]', { fromName: 'Support' });
MIT License
1,468 followers · starred Jan 2023
1,184 followers · starred Mar 2026
1,059 followers · starred Sep 2026
52 followers · starred Nov 2025
Framework guides
Next.js - Remix - Nuxt - Express - RedwoodJS - Hono - Bun - Astro
Node.js library for the Resend API.
npm install resend
# or
yarn add resend
Send email with:
First, you need to get an API key, which is available in the Resend Dashboard.
import { Resend } from 'resend';
const resend = new Resend('re_xxxx...xxxxxx');
Send your first email:
const { data } = await resend.emails.send({
from: '[email protected]',
to: '[email protected]',
replyTo: '[email protected]',
subject: 'hello world',
text: 'it works!',
});
console.log(`Email ${data.id} has been sent`);
[!NOTE]
In order to send from your own domain, you will first need to verify your domain in the Resend Dashboard.
Send an email custom HTML content:
const { data } = await resend.emails.send({
from: '[email protected]',
to: '[email protected]',
replyTo: '[email protected]',
subject: 'hello world',
html: '<strong>it works!</strong>',
});
console.log(`Email ${data.id} with custom HTML content has been sent.`);
Start by creating your email template as a React component.
import React from 'react';
export default function EmailTemplate({ firstName, product }) {
return (
<div>
<h1>Welcome, {firstName}!</h1>
<p>Thanks for trying {product}. We’re thrilled to have you on board.</p>
</div>
);
}
Then import the template component and pass it to the react property.
import EmailTemplate from '../components/EmailTemplate';
const { data } = await resend.emails.send({
from: '[email protected]',
to: '[email protected]',
replyTo: '[email protected]',
subject: 'hello world',
react: <EmailTemplate firstName="John" product="MyApp" />,
});
console.log(`Email ${data.id} with a React template has been sent`);
[!NOTE] If you're sending emails from a file that doesn't have JSX transpilation set up (e.g., in a
.js/.tsfile instead of JSX/TSX), use React'sjsxruntime function instead of passing the component as JSX:import { jsx } from 'react/jsx-runtime' import EmailTemplate from '../components/EmailTemplate'; await resend.emails.send({ from: '[email protected]', to: '[email protected]', replyTo: '[email protected]', subject: 'hello world', react: jsx(EmailTemplate, { firstName:"John", product:"MyApp" }), });
Every method takes request options as its last argument. Use signal to cancel a request or set a timeout, and headers to add request headers:
const controller = new AbortController();
await resend.emails.send({ ... }, { signal: controller.signal });
// With a timeout (Node.js 18+) and a custom header
await resend.emails.list(
{ limit: 50 },
{ signal: AbortSignal.timeout(5000), headers: { 'X-Trace-Id': traceId } },
);
An aborted request returns a network-failure error with a null status code. To tell a cancel apart from a real failure, check the status code and signal.aborted, and do not match on the error message:
const signal = AbortSignal.timeout(5000);
const { error } = await resend.emails.list({}, { signal });
if (error?.statusCode === null && signal.aborted) {
// The request was cancelled or timed out.
}
When a method has an optional argument before the request options, pass undefined in its place:
await resend.broadcasts.send(broadcastId, undefined, { signal });
A chained publish() sends a separate request, so it takes its own options:
await resend.templates
.create({ name: 'welcome', html: '<p>Hi</p>' }, { signal })
.publish({ signal });
Create an inbox and list its threads:
const { data: inbox } = await resend.inboxes.create({
emailAddress: '[email protected]',
name: 'support',
});
const { data: threads } = await resend.inboxes.threads.list({
inboxId: inbox.id,
folders: ['inbox'],
});
Every inbox method also accepts the inbox email address instead of its ID:
const { data: threads } = await resend.inboxes.threads.list({
emailAddress: '[email protected]',
folders: ['inbox'],
});
await resend.inboxes.update('[email protected]', { fromName: 'Support' });
MIT License
1,468 followers · starred Jan 2023
1,184 followers · starred Mar 2026
1,059 followers · starred Sep 2026
52 followers · starred Nov 2025