Back to blog

    How to Add a Contact Form to Astro

    Astro sites are static by default — there's no built-in way to receive form submissions. Here's how to add a working contact form to any Astro site in under 10 minutes.

    How to Add a Contact Form to Astro

    Astro builds fast, static sites. The downside: no backend means no built-in way to handle form submissions. This guide shows how to add a contact form to any Astro site — whether you're using static output, SSR, or a UI framework like React or Vue.

    The Options

    1. Plain HTML form — works on any static Astro site, zero JavaScript
    2. Client-side fetch — stay-on-page UX with a loading state
    3. Astro SSR route — server-side handling with output: "server"

    All three submit to Formboost, which handles storage, spam filtering, and email notifications.

    Option 1: Plain HTML Form (Simplest)

    In any .astro file, add:

    1---
    2// src/pages/contact.astro
    3---
    4
    5<html lang="en">
    6  <head><title>Contact</title></head>
    7  <body>
    8    <h1>Contact Us</h1>
    9
    10    <form action="https://formboost.app/f/YOUR_ENDPOINT_ID" method="POST">
    11      <label for="name">Name</label>
    12      <input id="name" type="text" name="name" required />
    13
    14      <label for="email">Email</label>
    15      <input id="email" type="email" name="email" required />
    16
    17      <label for="message">Message</label>
    18      <textarea id="message" name="message" rows="5"></textarea>
    19
    20      <button type="submit">Send</button>
    21    </form>
    22  </body>
    23</html>

    Replace YOUR_ENDPOINT_ID with your Formboost endpoint ID. Works with output: "static" (the default) — no JavaScript required.

    Option 2: Client-Side Fetch (Stay on Page)

    Add a <script> tag to handle the submission without leaving the page:

    1---
    2// src/pages/contact.astro
    3---
    4
    5<html lang="en">
    6  <head><title>Contact</title></head>
    7  <body>
    8    <h1>Contact Us</h1>
    9
    10    <form id="contact-form">
    11      <input type="text" name="name" required placeholder="Name" />
    12      <input type="email" name="email" required placeholder="Email" />
    13      <textarea name="message" rows="5" placeholder="Message"></textarea>
    14      <button type="submit">Send</button>
    15      <p id="status" hidden></p>
    16    </form>
    17
    18    <script>
    19      const form = document.getElementById('contact-form') as HTMLFormElement;
    20      const status = document.getElementById('status')!;
    21
    22      form.addEventListener('submit', async (e) => {
    23        e.preventDefault();
    24        const data = Object.fromEntries(new FormData(form));
    25
    26        try {
    27          const res = await fetch('https://formboost.app/f/YOUR_ENDPOINT_ID', {
    28            method: 'POST',
    29            headers: { 'Content-Type': 'application/json' },
    30            body: JSON.stringify(data),
    31          });
    32
    33          if (res.ok) {
    34            status.textContent = 'Message sent! We\'ll be in touch.';
    35            status.style.color = 'green';
    36            form.reset();
    37          } else {
    38            throw new Error();
    39          }
    40        } catch {
    41          status.textContent = 'Something went wrong. Please try again.';
    42          status.style.color = 'red';
    43        }
    44
    45        status.hidden = false;
    46      });
    47    </script>
    48  </body>
    49</html>

    Option 3: React Component in Astro

    If you have React enabled (@astrojs/react), use a React component for the form:

    1// src/components/ContactForm.tsx
    2import { useState } from 'react';
    3
    4export default function ContactForm() {
    5  const [status, setStatus] = useState<'idle' | 'sending' | 'sent' | 'error'>('idle');
    6
    7  async function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
    8    e.preventDefault();
    9    setStatus('sending');
    10
    11    const data = Object.fromEntries(new FormData(e.currentTarget));
    12
    13    try {
    14      const res = await fetch('https://formboost.app/f/YOUR_ENDPOINT_ID', {
    15        method: 'POST',
    16        headers: { 'Content-Type': 'application/json' },
    17        body: JSON.stringify(data),
    18      });
    19
    20      if (!res.ok) throw new Error();
    21      setStatus('sent');
    22    } catch {
    23      setStatus('error');
    24    }
    25  }
    26
    27  if (status === 'sent') return <p>Thanks! We'll be in touch soon.</p>;
    28
    29  return (
    30    <form onSubmit={handleSubmit}>
    31      <input name="name" type="text" required placeholder="Name" />
    32      <input name="email" type="email" required placeholder="Email" />
    33      <textarea name="message" rows={5} placeholder="Message" />
    34      {status === 'error' && <p>Something went wrong. Try again.</p>}
    35      <button type="submit" disabled={status === 'sending'}>
    36        {status === 'sending' ? 'Sending…' : 'Send'}
    37      </button>
    38    </form>
    39  );
    40}

    Then in your Astro page:

    1---
    2import ContactForm from '../components/ContactForm.tsx';
    3---
    4
    5<ContactForm client:load />

    client:load is the Astro directive that hydrates the component on the client side.

    Custom Redirect After Submission

    To redirect to a custom thank-you page after submission, add a hidden field:

    1<input type="hidden" name="_redirect" value="https://yoursite.com/thank-you" />

    Getting Your Endpoint

    1. Sign up at dashboard.formboost.app — free, no credit card
    2. Create a new endpoint
    3. Copy the URL and replace YOUR_ENDPOINT_ID in the examples above

    Get started with Formboost →