How to Connect an Astro Contact Form With Email

How to connect an Astro contact form with email using a secure API endpoint

Astro makes it easy to create fast, lightweight websites, but a standard contact form does not automatically deliver messages to your inbox. The form can collect a visitor’s name, email address, and message in the browser, but it still needs a secure server-side process or third-party service to send that information by email.

In this guide, you will learn how to connect an Astro contact form with email using a secure Astro API endpoint and Resend. You will also learn how to protect your API key, validate submissions, prevent common spam, troubleshoot errors, and choose an alternative solution when your Astro website uses fully static hosting.

This method is suitable for:

  • Business websites
  • Developer portfolios
  • Agency websites
  • Landing pages
  • Service-based websites
  • Lead-generation websites

What You Need Before Connecting the Form

Requirements for connecting an Astro contact form to an email service

Before starting, make sure you have the following:

  • A working Astro project
  • Node.js and npm installed
  • An email API provider account
  • A verified domain or access to your DNS settings
  • A destination email address
  • A hosting platform that supports server-side functions

This tutorial uses Resend because it provides a simple Node.js package and a developer-friendly email API. However, the same general process can be adapted for Brevo, SendGrid, Mailgun, Postmark, or another reliable provider.

Astro pages are normally generated as static files. Because the email must be sent when a visitor submits the form, your contact endpoint must run dynamically on the server.

Important: Never place your email API key in browser-accessible HTML, JavaScript, or Astro component code. Anyone visiting the website may be able to discover and misuse it.

When converting an existing front-end project into WordPress, you may also find this guide on how to convert a React website into a lightweight WordPress theme helpful.

Create the Astro Contact Form

Astro contact form with name, email, message, and validation fields

Create a file named contact.astro inside your src/pages directory.

The form will send the visitor’s information to an Astro API endpoint located at /api/contact.

---
// src/pages/contact.astro
---

<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta
      name="viewport"
      content="width=device-width, initial-scale=1.0"
    />
    <title>Contact Us</title>
  </head>

  <body>
    <main>
      <h1>Contact Us</h1>

      <form action="/api/contact" method="POST">
        <div>
          <label for="name">Name</label>
          <input
            id="name"
            name="name"
            type="text"
            minlength="2"
            maxlength="100"
            required
          />
        </div>

        <div>
          <label for="email">Email Address</label>
          <input
            id="email"
            name="email"
            type="email"
            maxlength="200"
            required
          />
        </div>

        <div>
          <label for="message">Message</label>
          <textarea
            id="message"
            name="message"
            minlength="10"
            maxlength="5000"
            required
          ></textarea>
        </div>

        <div class="website-field" aria-hidden="true">
          <label for="website">Website</label>
          <input
            id="website"
            name="website"
            type="text"
            tabindex="-1"
            autocomplete="off"
          />
        </div>

        <button type="submit">Send Message</button>
      </form>
    </main>
  </body>
</html>

<style>
  .website-field {
    position: absolute;
    left: -9999px;
  }
</style>

Every field must have a name attribute. Without it, the field’s value will not be included in the submitted form data.

The following attributes improve usability:

  • required prevents empty submissions.
  • type="email" checks the basic email format.
  • minlength prevents extremely short entries.
  • maxlength limits excessively large submissions.
  • <label> elements improve accessibility.

The hidden website field is a basic honeypot. Normal visitors will not complete it, but automated bots may fill it in.

Browser validation improves the user experience, but it is not sufficient for security. The submitted data must also be validated on the server.

Build the Server-Side Email Endpoint

Astro server endpoint validating contact form data and sending email

Install the Resend package from your Astro project directory:

npm install resend

Create the following file:

src/pages/api/contact.ts

Add this code:

import type { APIRoute } from "astro";
import { Resend } from "resend";

export const prerender = false;

const apiKey = import.meta.env.RESEND_API_KEY;
const contactToEmail = import.meta.env.CONTACT_TO_EMAIL;
const contactFromEmail = import.meta.env.CONTACT_FROM_EMAIL;

const resend = new Resend(apiKey);

function escapeHtml(value: string): string {
  return value
    .replaceAll("&", "&amp;")
    .replaceAll("<", "&lt;")
    .replaceAll(">", "&gt;")
    .replaceAll('"', "&quot;")
    .replaceAll("'", "&#039;");
}

function isValidEmail(email: string): boolean {
  return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}

export const POST: APIRoute = async ({ request }) => {
  try {
    if (!apiKey || !contactToEmail || !contactFromEmail) {
      console.error("Contact form environment variables are missing.");

      return new Response("Server configuration error.", {
        status: 500,
      });
    }

    const formData = await request.formData();

    const name = String(formData.get("name") ?? "").trim();
    const email = String(formData.get("email") ?? "").trim();
    const message = String(formData.get("message") ?? "").trim();
    const website = String(formData.get("website") ?? "").trim();

    if (website) {
      return new Response(null, { status: 204 });
    }

    if (
      name.length < 2 ||
      name.length > 100 ||
      !isValidEmail(email) ||
      email.length > 200 ||
      message.length < 10 ||
      message.length > 5000
    ) {
      return new Response("Please check the submitted information.", {
        status: 400,
      });
    }

    const safeName = escapeHtml(name);
    const safeEmail = escapeHtml(email);
    const safeMessage = escapeHtml(message).replaceAll("\n", "<br />");

    const { error } = await resend.emails.send({
      from: contactFromEmail,
      to: contactToEmail,
      replyTo: email,
      subject: `New website message from ${name}`,
      html: `
        <h2>New Contact Form Submission</h2>
        <p><strong>Name:</strong> ${safeName}</p>
        <p><strong>Email:</strong> ${safeEmail}</p>
        <p><strong>Message:</strong></p>
        <p>${safeMessage}</p>
      `,
      text: `
        New Contact Form Submission

        Name: ${name}
        Email: ${email}

        Message:
        ${message}
      `,
    });

    if (error) {
      console.error("Email provider error:", error);

      return new Response(
        "Your message could not be sent. Please try again.",
        { status: 502 },
      );
    }

    return Response.redirect(
      new URL("/thank-you/", request.url),
      303,
    );
  } catch (error) {
    console.error("Contact endpoint error:", error);

    return new Response(
      "An unexpected error occurred. Please try again.",
      { status: 500 },
    );
  }
};

export const ALL: APIRoute = async () => {
  return new Response("Method not allowed.", {
    status: 405,
    headers: {
      Allow: "POST",
    },
  });
};

The following line is essential:

export const prerender = false;

It tells Astro that the endpoint must run on demand rather than being generated as a static file.

The endpoint also:

  1. Reads the submitted form data.
  2. Rejects likely spam submissions.
  3. Validates field lengths and email formatting.
  4. Escapes user-provided content.
  5. Sends HTML and plain-text email versions.
  6. Handles provider and server errors.
  7. Redirects successful users to a thank-you page.

Escaping the submitted values is important because visitors should not be able to inject unwanted HTML into your notification email.

Add Environment Variables and a Server Adapter

Astro environment variables, verified email domain, and server adapter setup

Create a .env file in the project root:

RESEND_API_KEY=re_your_private_api_key
CONTACT_TO_EMAIL=you@example.com
CONTACT_FROM_EMAIL=Website Contact <contact@yourdomain.com>

Replace the example values with your actual API key and email addresses.

Add the environment file to .gitignore:

.env
.env.*
!.env.example

This helps prevent private credentials from being uploaded to a public GitHub repository.

You must also add the same environment variables to your hosting platform. Local .env values are not automatically transferred when the website is deployed.

Verify Your Sending Domain

For reliable email delivery, verify your domain through your email provider and add the required DNS records.

Use an email address from the verified domain as the sender:

contact@yourdomain.com

Do not use the visitor’s email as the sender. Instead, place it in the replyTo property. This allows you to reply directly while maintaining proper email authentication.

Install a Server Adapter

Install the adapter that matches your hosting platform.

For Netlify:

npx astro add netlify

For Vercel:

npx astro add vercel

For Cloudflare:

npx astro add cloudflare

For a Node.js server:

npx astro add node

Install only one adapter based on your deployment environment.

Create a simple success page at:

src/pages/thank-you.astro
---
---

<html lang="en">
  <head>
    <title>Thank You</title>
  </head>

  <body>
    <main>
      <h1>Thank You</h1>
      <p>Your message has been sent successfully.</p>
      <a href="/">Return to the homepage</a>
    </main>
  </body>
</html>

Protect the Astro Contact Form From Spam

Spam protection and server-side security for an Astro contact form

Any public form can eventually attract automated spam. The honeypot field provides basic protection, but a production website should use multiple safeguards.

Recommended protections include:

  1. Server-side validation: Validate every submitted field on the server.
  2. Rate limiting: Restrict repeated requests from the same IP address.
  3. Maximum field lengths: Prevent unusually large submissions.
  4. CAPTCHA or Turnstile: Add bot verification when spam becomes frequent.
  5. Origin checking: Reject requests from unauthorized websites.
  6. Generic error messages: Avoid exposing internal server details.
  7. Server-side logging: Log failed requests without unnecessarily storing personal data.
  8. Submission timing checks: Reject forms completed unrealistically quickly.

Security Tip: Never send an API key to the browser or include it in client-side JavaScript.

You should also consider adding a privacy notice near the form, especially when collecting names, email addresses, telephone numbers, or other personal information.

Test the Form and Fix Common Errors

Testing and debugging an Astro contact form email integration

Start the local Astro development server:

npm run dev

Open your contact page and submit a test message.

Confirm that:

  • The browser sends a POST request.
  • The endpoint returns a success response or redirect.
  • The message reaches the correct inbox.
  • The reply button uses the visitor’s email.
  • Invalid submissions return an error.
  • The API key is not visible in browser tools.
  • The form works after production deployment.

Use the browser Network panel and your hosting platform’s function logs when investigating errors.

Problem Likely Cause Recommended Solution
API endpoint returns 404 Incorrect route or static-only deployment Check the file path and server adapter
Endpoint returns 500 Missing environment variables Add the variables to the hosting dashboard
Provider returns 403 Sender domain is not verified Verify the domain and sender address
Form reloads without sending Incorrect action or request method Use /api/contact and method="POST"
Email reaches spam Authentication records are missing Configure the required DNS records
Duplicate emails arrive Multiple submissions are being sent Disable the button after submission
Local form works but live form fails Production configuration is different Check live variables and function logs

Always test the deployed website before considering the integration complete. Local testing cannot fully verify production environment variables, DNS authentication, serverless behavior, or real email delivery.

Alternative Ways to Connect an Astro Form With Email

Comparison of Astro contact form email integration methods

A custom API endpoint provides control and flexibility, but it is not the only available solution.

Astro Actions

Astro Actions provide structured server-side functions, input validation, and standardized error handling. They can be useful for modern Astro projects that contain several interactive forms.

Hosted Form Services

Services such as Formspree, Basin, Getform, and Netlify Forms can process submissions without a custom email endpoint.

They are useful for:

  • Small static websites
  • Beginner projects
  • Simple portfolio forms
  • Websites with low submission volume

However, free plans may include submission limits, branding, reduced control, or fewer spam-protection features.

External Serverless Functions

You can keep the main Astro website static and send submissions to a separate serverless function hosted on platforms such as Vercel, Netlify, Cloudflare, or AWS.

This approach provides flexibility but creates an additional service that must be secured and maintained.

Method Best For Main Limitation
Astro API endpoint Custom business forms and complete control Requires server-compatible hosting
Astro Actions Modern Astro projects with multiple forms Requires backend runtime configuration
Hosted form service Beginners and simple static sites Usage limits and less customization
External function Static hosting with a separate backend Additional configuration and maintenance

For most small business websites, an Astro API endpoint or Astro Action offers the best balance of security, customization, and reliability.

Frequently Asked Questions

Can an Astro contact form send email without a backend?

A browser form cannot securely use a private email API key by itself. You need an Astro endpoint, Astro Action, serverless function, or hosted form-processing service.

Can I connect an Astro contact form to Gmail?

Yes. Your email provider can deliver form notifications to a Gmail address. Do not place Gmail credentials or API keys inside browser-accessible code.

Why does my Astro contact form work locally but not online?

Your production environment variables may be missing, the incorrect server adapter may be installed, or the hosting platform may not support the selected server runtime.

Can I use PHP to process an Astro contact form?

Yes, but only when your hosting server supports PHP. Many Astro websites use Node.js endpoints or serverless functions instead.

Is Resend required for an Astro contact form?

No. You can use Brevo, SendGrid, Mailgun, Postmark, Formspree, Netlify Forms, or another trusted provider.

How can I reduce contact form spam?

Use server-side validation, rate limiting, a honeypot field, submission timing checks, field-length restrictions, and CAPTCHA or Cloudflare Turnstile when needed.

Should I use the visitor’s email as the sender address?

No. Use an address from your verified domain as the sender and place the visitor’s email in the reply-to field.

Conclusion

Learning how to connect an Astro contact form with email involves more than adding a few form fields. The form needs a secure server-side handler, protected environment variables, an authenticated email provider, proper validation, spam protection, and a compatible hosting environment.

The API endpoint method covered in this guide provides a strong foundation. It validates visitor information, blocks basic bot submissions, safely creates the email, handles provider errors, and redirects successful users to a confirmation page.

Before launching your form, test it on the live website, verify your sending domain, review your server logs, and confirm that no private API credentials are publicly accessible.

A carefully configured contact form provides visitors with a reliable way to reach your business while protecting your website and email account from unnecessary security and delivery problems.

Comments