Astro Contact Form: Send Email with Resend
To build an Astro contact form that sends email with Resend, you create a standard HTML form in an Astro component, handle the submission with an API endpoint (serverless function), and call Resend’s API to send the message. You need an Astro project with server-side rendering (SSR) enabled, a Resend account and API key, and a verified domain (or use Resend’s test mode).
Background: Why Resend for Astro Forms
Astro is a static-first framework, so a form alone can’t send email without a backend. Resend is an email API that lets you send messages from a serverless function, which fits Astro’s hybrid model. You can keep your site static and add a single dynamic endpoint for the form. Resend offers a simple REST API and official Node.js SDK, making it a common choice for developers who want transactional email without managing an SMTP server.
Prerequisites and Setup
- An Astro project (version 4 or 5 recommended) with the
@astrojs/nodeadapter or a similar adapter that supports SSR. - A Resend account and an API key (found in the dashboard).
- A verified domain if you want to send from your own address; otherwise use the shared
onboarding@resend.devfor testing. - Node.js 18+ and npm or your preferred package manager.
How It Works: Step-by-Step
- Enable SSR – In
astro.config.mjs, add the adapter and setoutput: 'server'(or useoutput: 'hybrid'to keep most pages static). - Install the Resend SDK – Run
npm install resend. - Create the form – Build a standard HTML form in an Astro component, with
actionpointing to your API route andmethod="POST". - Create the API route – In
src/pages/api/send.ts, export aPOSThandler that receives the form data, validates it, and callsresend.emails.send(). - Handle the response – Return a JSON response, then redirect the user to a thank-you page or show a success message with client-side JavaScript.
Code Example: Minimal Working Form
Here’s a complete example you can adapt. First, the form component (src/components/ContactForm.astro):
<form method="POST" action="/api/send">
<label>Name<input type="text" name="name" required></label>
<label>Email<input type="email" name="email" required></label>
<label>Message<textarea name="message" required></textarea></label>
<button type="submit">Send</button>
</form>
Then the API route (src/pages/api/send.ts):
import { Resend } from 'resend';
export const prerender = false;
export async function POST({ request }) {
const formData = await request.formData();
const name = formData.get('name');
const email = formData.get('email');
const message = formData.get('message');
const resend = new Resend(import.meta.env.RESEND_API_KEY);
const { error } = await resend.emails.send({
from: 'Your Site <onboarding@resend.dev>',
to: ['you@example.com'],
subject: `New contact from ${name}`,
text: `Name: ${name}\nEmail: ${email}\nMessage: ${message}`,
});
if (error) {
return new Response(JSON.stringify({ error }), { status: 500 });
}
return new Response(null, { status: 302, headers: { Location: '/thank-you' } });
}
Remember to store your API key in an environment variable (.env) and never expose it in client code.
Common Problems and How to Avoid Them
- SSR not enabled – Without an adapter, Astro builds static pages only, and the API route won’t work. Add the Node adapter and set
output: 'server'. - Form data missing – Ensure each input has a
nameattribute; otherwiseformData.get()returnsnull. - Email not sending – Check your API key, the
fromaddress (must be verified or use the test address), and your domain verification status in Resend. - CORS or 404 errors – Make sure the API route is in the correct folder and that you’re posting to the right URL.
- Spam – Add a honeypot field or use a CAPTCHA service to reduce abuse.
Alternatives to Resend
Resend is one option, but there are others: SendGrid, Mailgun, Postmark, and Amazon SES all offer REST APIs. If you prefer a form service, Formspree or Getform can handle submissions without any backend code. For a self-hosted approach, you could use Nodemailer with your own SMTP server, but that requires maintaining a mail server.
Costs and Limitations
Resend offers a free tier with a limited number of emails per month (typically 3,000 for testing), after which you pay per email. The free tier includes the shared domain, but for production you’ll need a verified domain. There’s no charge for the API calls themselves; you only pay for emails sent. Keep in mind that free tiers often have lower sending limits, so if you expect high volume, check the pricing page.
Safety and Best Practices
- Always validate and sanitize user input on the server to prevent injection attacks.
- Never log the full API key or expose it in client-side code.
- Use HTTPS in production to protect form data in transit.
- Add rate limiting to your API endpoint to prevent abuse.
- Test with the Resend test mode before going live.
Frequently Asked Questions
Do I need a paid Resend plan to send from my own domain?
No, you can verify a domain on the free tier and send from it, but there’s a monthly sending limit. For low-volume contact forms, the free tier is usually sufficient.
Can I use Resend with Astro’s static output?
No, you need server-side rendering for the API endpoint. However, you can use output: 'hybrid' to keep most pages static while the API route runs dynamically.
How do I handle file uploads in the form?
Resend’s API supports attachments, but you’ll need to parse the file in your endpoint using something like formidable or busboy. Then base64-encode the file and pass it to the attachments array.
What if my form is on a static hosting service like Netlify?
You can still use Resend by creating a serverless function (e.g., Netlify Functions) that calls the Resend API. The Astro code remains the same, but the endpoint is deployed as a function.
Is Resend reliable for production?
Yes, Resend is designed for production use, with a high deliverability rate and a robust API. Many companies use it for transactional emails. Always test thoroughly before launch.
Can I send HTML emails with Resend?
Yes, the html field in the send request accepts HTML content. You can use a template string or a library like react-email to generate the HTML.
Related
Method: the standard formula for this calculation, rounded as each result states. See how the calculators work or report an error.