How to Integrate Resend with Astro: Complete Guide
Step-by-step guide to integrating Resend with your Astro website. Setup, configuration, and best practices.
How to Integrate Resend with Astro: Complete Guide
Resend is a modern email API built for developers, created by the same team behind React Email. It provides a clean API for sending transactional emails, supports custom domains, and works well with modern frameworks. Combined with Astro's server-side capabilities, Resend lets you send emails from contact forms, sign-up flows, and notification systems directly from your Astro project.
This guide covers setting up Resend in an Astro project, sending plain text and HTML emails, using React Email templates, and building a working contact form.
Prerequisites
You will need:
- Node.js 18+ installed
- An existing Astro project with SSR enabled (v3.0+)
- A Resend account (free tier at resend.com, 100 emails/day)
- A Resend API key (found in your Resend dashboard under API Keys)
- A verified domain (optional but recommended for production)
Installation
Install the Resend SDK:
npm install resend
If you want to use React Email templates for styled emails:
npm install @react-email/components react react-dom
Make sure you have an SSR adapter installed:
npx astro add node
Configuration
Environment Variables
Add your Resend API key to .env:
RESEND_API_KEY=re_your_api_key_here
Creating the Resend Client
Set up a reusable Resend instance:
// src/lib/resend.ts
import { Resend } from 'resend';
export const resend = new Resend(import.meta.env.RESEND_API_KEY);
Astro Config
Enable server-side rendering:
// astro.config.mjs
import { defineConfig } from 'astro/config';
import node from '@astrojs/node';
export default defineConfig({
output: 'hybrid',
adapter: node({ mode: 'standalone' }),
});
Common Patterns
Sending a Simple Email
Create an API endpoint that sends a plain text email:
// src/pages/api/send-email.ts
import type { APIRoute } from 'astro';
import { resend } from '../../lib/resend';
export const POST: APIRoute = async ({ request }) => {
const body = await request.json();
try {
const { data, error } = await resend.emails.send({
from: 'Your Site <noreply@yourdomain.com>',
to: body.to,
subject: body.subject,
text: body.message,
});
if (error) {
return new Response(JSON.stringify({ error }), { status: 400 });
}
return new Response(JSON.stringify({ id: data?.id }), { status: 200 });
} catch (err) {
return new Response(JSON.stringify({ error: 'Failed to send email' }), { status: 500 });
}
};
Building a Contact Form
Create a contact page with a form that sends emails through your API:
---
// src/pages/contact.astro
export const prerender = false;
let success = false;
let errorMessage = '';
if (Astro.request.method === 'POST') {
try {
const formData = await Astro.request.formData();
const name = formData.get('name') as string;
const email = formData.get('email') as string;
const message = formData.get('message') as string;
const { resend } = await import('../lib/resend');
const { error } = await resend.emails.send({
from: 'Contact Form <noreply@yourdomain.com>',
to: 'you@yourdomain.com',
replyTo: email,
subject: `Contact from ${name}`,
html: `
<h2>New Contact Form Submission</h2>
<p><strong>Name:</strong> ${name}</p>
<p><strong>Email:</strong> ${email}</p>
<p><strong>Message:</strong></p>
<p>${message}</p>
`,
});
if (error) {
errorMessage = 'Failed to send message. Please try again.';
} else {
success = true;
}
} catch {
errorMessage = 'Something went wrong.';
}
}
---
<html>
<body>
<h1>Contact Us</h1>
{success && <p style="color: green;">Message sent successfully!</p>}
{errorMessage && <p style="color: red;">{errorMessage}</p>}
<form method="POST">
<label>Name <input type="text" name="name" required /></label>
<label>Email <input type="email" name="email" required /></label>
<label>Message <textarea name="message" rows="5" required></textarea></label>
<button type="submit">Send Message</button>
</form>
</body>
</html>
Using React Email Templates
For more polished emails, create React Email components:
// src/emails/WelcomeEmail.tsx
import { Html, Head, Body, Container, Text, Button, Heading } from '@react-email/components';
interface WelcomeEmailProps {
name: string;
loginUrl: string;
}
export default function WelcomeEmail({ name, loginUrl }: WelcomeEmailProps) {
return (
<Html>
<Head />
<Body style={{ backgroundColor: '#f6f9fc', fontFamily: 'sans-serif' }}>
<Container style={{ padding: '40px 20px', maxWidth: '560px', margin: '0 auto' }}>
<Heading>Welcome, {name}!</Heading>
<Text>Thanks for signing up. Click below to get started.</Text>
<Button
href={loginUrl}
style={{
backgroundColor: '#5046e5',
color: '#fff',
padding: '12px 24px',
borderRadius: '6px',
textDecoration: 'none',
}}
>
Get Started
</Button>
</Container>
</Body>
</Html>
);
}
Send it from an API route:
// src/pages/api/welcome.ts
import type { APIRoute } from 'astro';
import { resend } from '../../lib/resend';
import WelcomeEmail from '../../emails/WelcomeEmail';
export const POST: APIRoute = async ({ request }) => {
const { name, email } = await request.json();
const { data, error } = await resend.emails.send({
from: 'App <welcome@yourdomain.com>',
to: email,
subject: `Welcome, ${name}!`,
react: WelcomeEmail({ name, loginUrl: 'https://yourdomain.com/login' }),
});
if (error) {
return new Response(JSON.stringify({ error }), { status: 400 });
}
return new Response(JSON.stringify({ id: data?.id }), { status: 200 });
};
Troubleshooting
"You can only send testing emails to your own email address": On the free tier without a verified domain, Resend restricts the to address to the email you signed up with. Add and verify a custom domain in the Resend dashboard to send to any address.
Emails going to spam: Set up SPF, DKIM, and DMARC records for your domain. Resend provides all three during the domain verification process. Also avoid using a from address on a domain you have not verified.
"from" address rejected: The sender address must use a domain you have verified in Resend, or use onboarding@resend.dev for testing. Using an unverified domain in the from field will return an error.
React Email template not rendering: Make sure react and react-dom are installed as dependencies. The React Email components need these to render. Also verify your .tsx file uses JSX syntax correctly.
Rate limiting on free tier: The free tier allows 100 emails per day and 1 email per second. For production, upgrade to a paid plan. Implement queuing in your application if you need to send bursts of emails.
Conclusion
Resend provides a developer-friendly email API that fits naturally into Astro's server-side architecture. The combination of Astro API routes and Resend's SDK makes it straightforward to add contact forms, transactional emails, and notification systems. Use React Email templates for polished, responsive email designs that render consistently across email clients. Start with the free tier and a verified domain to get production-quality email delivery from day one.
Related Articles
How to Use Algolia with Astro: Complete Guide
Step-by-step guide to integrating Algolia with your Astro website.
How to Integrate Auth0 with Astro: Complete Guide
Step-by-step guide to integrating Auth0 with your Astro website. Setup, configuration, and best practices.
How to Use AWS Amplify with Astro: Complete Guide
Step-by-step guide to integrating AWS Amplify with your Astro website.