Skip to content
FormSubmit

Astro

How to add a contact form to an Astro site

Add a working contact form to a static Astro site with zero JavaScript — or as an island with inline success messages.

1 min readUpdated Sep 28, 2026

In Astro, the easiest contact form is a plain .astro component whose <form> posts to a FormSubmit endpoint. It ships zero JavaScript, works with Astro's default static output (no SSR adapter), and you still get email notifications, spam filtering and integrations.

1. Create an endpoint

Sign in, create a form and copy the endpoint URL.

2. Create the component

src/components/ContactForm.astro:

src/components/ContactForm.astro
---
// src/components/ContactForm.astro
const endpoint = "https://formsubmit.app/f/YOUR_FORM_ID";
---

<form action={endpoint} method="POST" class="mx-auto max-w-lg space-y-5">
  <div class="space-y-1.5">
    <label for="fs-name" class="block text-sm font-medium text-gray-900">Name</label>
    <input id="fs-name" type="text" name="name" placeholder="Jane Doe" required class="block w-full rounded-lg border border-gray-300 px-3 py-2 text-sm shadow-sm focus:border-indigo-500 focus:outline-none focus:ring-2 focus:ring-indigo-500/20">
  </div>
  <div class="space-y-1.5">
    <label for="fs-email" class="block text-sm font-medium text-gray-900">Email</label>
    <input id="fs-email" type="email" name="email" placeholder="jane@example.com" required class="block w-full rounded-lg border border-gray-300 px-3 py-2 text-sm shadow-sm focus:border-indigo-500 focus:outline-none focus:ring-2 focus:ring-indigo-500/20">
  </div>
  <div class="space-y-1.5">
    <label for="fs-message" class="block text-sm font-medium text-gray-900">Message</label>
    <textarea id="fs-message" name="message" rows="5" placeholder="How can we help?" required class="block w-full rounded-lg border border-gray-300 px-3 py-2 text-sm shadow-sm focus:border-indigo-500 focus:outline-none focus:ring-2 focus:ring-indigo-500/20"></textarea>
  </div>
  <!-- Honeypot: leave this hidden field empty to catch bots -->
  <input type="text" name="_gotcha" tabindex="-1" autocomplete="off" style="display:none">
  <input type="hidden" name="_redirect" value={new URL("/thanks", Astro.url).href} />
  <button type="submit" class="inline-flex w-full items-center justify-center rounded-lg bg-gray-900 px-4 py-2.5 text-sm font-medium text-white hover:bg-gray-800 disabled:opacity-50">Send</button>
</form>

The _redirect field uses Astro.url to build an absolute URL to /thanks on whatever domain the page is served from — handy for preview deployments.

3. Create the thank-you page

src/pages/thanks.astro
---
import Layout from "../layouts/Layout.astro";
---
<Layout title="Thanks!">
  <h1>Thanks — we got your message.</h1>
  <p>We'll reply within one business day.</p>
</Layout>

4. Use it

src/pages/contact.astro
---
import Layout from "../layouts/Layout.astro";
import ContactForm from "../components/ContactForm.astro";
---
<Layout title="Contact">
  <h1>Contact</h1>
  <ContactForm />
</Layout>

Inline success without leaving the page

Add a small script to the component. Astro bundles and deduplicates it automatically:

astro
<script>
  const form = document.querySelector("form[data-fs]") as HTMLFormElement;
  const renderedAt = Date.now();
  form?.addEventListener("submit", async (e) => {
    e.preventDefault();
    const data = new FormData(form);
    data.set("_ts", String(renderedAt));
    const res = await fetch(form.action, { method: "POST", body: data, headers: { Accept: "application/json" } });
    const json = await res.json();
    form.outerHTML = json.ok ? `<p>${json.message}</p>` : `<p role="alert">${json.error.message}</p>`;
  });
</script>

(Add data-fs to the <form> element.)

Islands

Prefer a framework component? Export React, Vue or Svelte from the form generator and render it with client:visible so it only hydrates when scrolled into view.

Before launch

Add your domain to the allowlist, and consider connecting Slack so new messages reach your team immediately.

Frequently asked questions

Do I need an SSR adapter?

No. The form posts directly to FormSubmit, so it works with Astro's default static output.

Can I use a React, Vue or Svelte island instead?

Yes. Use the React, Vue or Svelte component from the form generator with client:load or client:visible.

Keep reading

Your form backend is 60 seconds away

Sign in with Google, create a form, paste the endpoint. Free forever for small sites — no credit card.