How to Add a Contact Form to Your Astro Website

4 min read
Static Forms Team

Astro is a modern web framework that generates fast, static HTML by default. While this is great for performance, it means your site has no built-in way to process form submissions. You need a form backend service to handle the server-side work — receiving data, sending emails, and protecting against spam.

In this tutorial, you will learn how to add a fully functional contact form to your Astro website using Static Forms. We will cover two approaches: a simple HTML form and a more interactive JavaScript-powered version.

Prerequisites

Before you begin, make sure you have:

Approach 1: Simple HTML Form

The simplest way to add a contact form to Astro is with a plain HTML form. No JavaScript required — the form submits directly to the Static Forms endpoint.

Create a new page at src/pages/contact.astro:

Astro
---
// src/pages/contact.astro
import Layout from '../layouts/Layout.astro';
---

<Layout title="Contact Us">
  <main>
    <h1>Contact Us</h1>
    <p>Send us a message and we will get back to you shortly.</p>

    <form action="https://api.staticforms.dev/submit" method="POST">
      <label for="name">Name</label>
      <input type="text" id="name" name="name" required />

      <label for="email">Email</label>
      <input type="email" id="email" name="email" required />

      <label for="message">Message</label>
      <textarea id="message" name="message" rows="5" required></textarea>

      <!-- Static Forms configuration -->
      <input type="hidden" name="apiKey" value="YOUR_API_KEY_HERE" />
      <input type="hidden" name="replyTo" value="@" />
      <input type="text" name="honeypot" style="display:none" />

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

Replace YOUR_API_KEY_HERE with your actual API key. The replyTo field set to @ uses the submitter's email as the reply-to address. The hidden honeypot field provides basic bot protection.

This approach works immediately with zero client-side JavaScript. When a user submits the form, they are redirected to a Static Forms confirmation page. You can customize this by adding a redirectTo hidden field with your own thank-you page URL.

Approach 2: Async Fetch with Client-Side JavaScript

For a smoother user experience without page redirects, you can use Astro's client-side scripting to submit the form via fetch:

Astro
---
// src/pages/contact.astro
import Layout from '../layouts/Layout.astro';
---

<Layout title="Contact Us">
  <main>
    <h1>Contact Us</h1>

    <div id="success" style="display:none; padding:16px; background:#d4edda; border-radius:4px; margin-bottom:16px;">
      Thank you! Your message has been sent.
    </div>
    <div id="error" style="display:none; padding:16px; background:#f8d7da; border-radius:4px; margin-bottom:16px;">
      Something went wrong. Please try again.
    </div>

    <form id="contactForm">
      <label for="name">Name</label>
      <input type="text" id="name" name="name" required />

      <label for="email">Email</label>
      <input type="email" id="email" name="email" required />

      <label for="message">Message</label>
      <textarea id="message" name="message" rows="5" required></textarea>

      <input type="text" name="honeypot" style="display:none" />

      <button type="submit" id="submitBtn">Send Message</button>
    </form>
  </main>
</Layout>

<script>
  const form = document.getElementById('contactForm') as HTMLFormElement;
  const submitBtn = document.getElementById('submitBtn') as HTMLButtonElement;
  const successEl = document.getElementById('success')!;
  const errorEl = document.getElementById('error')!;

  form.addEventListener('submit', async (e) => {
    e.preventDefault();
    submitBtn.disabled = true;
    submitBtn.textContent = 'Sending...';
    successEl.style.display = 'none';
    errorEl.style.display = 'none';

    const formData = new FormData(form);
    const data = Object.fromEntries(formData);

    try {
      const response = await fetch('https://api.staticforms.dev/submit', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          ...data,
          apiKey: import.meta.env.PUBLIC_STATIC_FORMS_KEY,
          replyTo: '@',
        }),
      });

      const result = await response.json();

      if (result.success) {
        successEl.style.display = 'block';
        form.reset();
      } else {
        throw new Error(result.message);
      }
    } catch {
      errorEl.style.display = 'block';
    } finally {
      submitBtn.disabled = false;
      submitBtn.textContent = 'Send Message';
    }
  });
</script>

Environment Variables

Instead of hardcoding your API key, store it in an environment variable. Create a .env file in your project root:

Plain Text
PUBLIC_STATIC_FORMS_KEY=your-api-key-here

Astro exposes variables prefixed with PUBLIC_ to client-side code via import.meta.env. This keeps your key out of source control while still making it available in the browser. Note that form API keys are safe to expose client-side since they are tied to your verified email address.

Adding reCAPTCHA Spam Protection

For stronger spam protection, add reCAPTCHA to your form. First, get your site key from the Google reCAPTCHA Admin Console, then configure your secret key in your Static Forms CAPTCHA settings.

Add the reCAPTCHA script to your layout's <head> and include the widget in your form:

HTML
<!-- In your layout head -->
<script src="https://www.google.com/recaptcha/api.js" async defer></script>

<!-- In your form, before the submit button -->
<div class="g-recaptcha" data-sitekey="YOUR_RECAPTCHA_SITE_KEY"></div>

When submitting via JavaScript, include the reCAPTCHA response token in your request body:

JavaScript
const recaptchaResponse = document.querySelector('[name="g-recaptcha-response"]').value;
// Add g-recaptcha-response: recaptchaResponse to your POST body

For a deeper dive into reCAPTCHA, see our understanding reCAPTCHA guide.

Astro-Specific Documentation

For more detailed Astro integration instructions, including component examples and advanced configuration, check out our Astro documentation page.

Next Steps

You now have a working contact form on your Astro site. Static Forms handles email delivery, spam protection, and submission storage so you can focus on building your site.

On the Pro plan, you also get access to file uploads, webhooks, auto-responders, and custom email templates. Sign up for Static Forms to get started with 500 free submissions per month.