How to Add a Contact Form to Your Astro Site
Last updated: March 2026
Astro is built for content-driven static sites. Its islands architecture renders pages as static HTML and only hydrates interactive components where you explicitly opt in. That design is great for performance, but it means there's no server-side runtime to process form submissions.
To add a working contact form to your Astro site, you need a form backend: a service that receives the POST request, stores the submission, and sends you a notification. This guide walks through the full setup with FormWit, including static vs SSR considerations, progressive enhancement with JavaScript, spam protection, and redirect handling.
Why Astro forms need a backend
In static mode (output: 'static'), Astro generates plain HTML files at build time. There's no Node.js process running when a visitor loads the page. When someone submits a form, the browser sends an HTTP POST to whatever URL is in the action attribute. If that URL is your static page, the CDN returns a 405 Method Not Allowed error.
Even in SSR mode (output: 'server'), you'd need to write an API route to handle the POST, validate the data, send the email, and store the submission. A form backend removes all of that work. You point the form at an external endpoint and the backend handles everything.
The key Astro-specific advantage: since forms live in the static HTML layer (not inside a hydrated island), they work out of the box with zero client-side JavaScript. The form is rendered at build time, and the browser handles the native form submission. No framework overhead required.
Step 1: Create the form component
Create a new file at src/components/ContactForm.astro:
---
const FORM_ID = "YOUR_FORM_ID";
---
<form
action={`https://app.formwit.com/api/s/${FORM_ID}`}
method="POST"
class="space-y-4"
>
<!-- Honeypot for spam -->
<input type="text" name="_gotcha" style="display:none" tabindex="-1" autocomplete="off" />
<!-- Redirect after submission -->
<input type="hidden" name="redirect_to" value="https://yoursite.com/thank-you" />
<div>
<label for="name" class="block font-medium mb-1">Name</label>
<input type="text" id="name" name="name" required
class="w-full border rounded-lg px-4 py-2" />
</div>
<div>
<label for="email" class="block font-medium mb-1">Email</label>
<input type="email" id="email" name="email" required
class="w-full border rounded-lg px-4 py-2" />
</div>
<div>
<label for="message" class="block font-medium mb-1">Message</label>
<textarea id="message" name="message" rows="5" required
class="w-full border rounded-lg px-4 py-2"></textarea>
</div>
<button type="submit"
class="bg-indigo-600 text-white px-6 py-2 rounded-lg font-medium hover:bg-indigo-700">
Send Message
</button>
</form> Replace YOUR_FORM_ID with the endpoint ID from your FormWit dashboard. Update the redirect_to value to your own thank-you page URL (or remove the field to use FormWit's default success page).
Step 2: Add it to a page
Use the component in any Astro page, for example src/pages/contact.astro:
---
import Layout from '../layouts/Layout.astro';
import ContactForm from '../components/ContactForm.astro';
---
<Layout title="Contact">
<main class="max-w-xl mx-auto py-20 px-4">
<h1 class="text-3xl font-bold mb-8">Contact Us</h1>
<ContactForm />
</main>
</Layout> This renders the form as static HTML at build time. No client: directive needed, no hydration, no JavaScript bundle. The browser's native form submission handles the POST request.
Step 3: Get your form endpoint
- Sign up for a free FormWit account
- Create a new form in the dashboard
- Copy the form endpoint ID
- Replace
YOUR_FORM_IDin the component
Static vs SSR mode
This approach works identically in both Astro output modes:
- Static (
output: 'static'): The form is rendered at build time as plain HTML. The browser sends the POST directly to FormWit. This is the simplest setup and the one most Astro sites use. - SSR (
output: 'server'): Same HTML form, same behavior. The form submission bypasses your Astro server entirely because theactionattribute points to an external URL. You don't need to create an API route. - Hybrid (
output: 'hybrid'): Same story. The form page can be pre-rendered or server-rendered. Either way, the form posts to FormWit, not to your Astro server.
Since the form uses a standard HTML action attribute pointing to an external endpoint, Astro's rendering mode is irrelevant. The form works the same way everywhere.
Progressive enhancement with JavaScript
The HTML form works without any JavaScript. But if you want a smoother experience (no page reload, inline success/error messages), you can add a client-side script that intercepts the submission:
<script>
const form = document.querySelector('form');
const button = form?.querySelector('button[type="submit"]');
form?.addEventListener('submit', async (e) => {
e.preventDefault();
if (!button) return;
button.disabled = true;
button.textContent = 'Sending...';
try {
const res = await fetch(form.action, {
method: 'POST',
body: new FormData(form),
});
if (res.ok) {
form.reset();
button.textContent = 'Sent';
// Re-enable after 3 seconds
setTimeout(() => {
button.disabled = false;
button.textContent = 'Send Message';
}, 3000);
} else {
button.textContent = 'Error - try again';
button.disabled = false;
}
} catch {
button.textContent = 'Error - try again';
button.disabled = false;
}
});
</script> In Astro, inline <script> tags are bundled and optimized automatically. This script is progressive enhancement: if JavaScript fails to load, the form still works via the native browser submission. The visitor gets redirected to FormWit's success page (or your custom redirect_to URL) instead of seeing the inline success message.
Redirect after submission
When using the native form submission (no JavaScript), FormWit redirects visitors to a default success page. To send them to your own page instead, add a hidden redirect_to field:
<input type="hidden" name="redirect_to" value="https://yoursite.com/thank-you" /> You can create a dedicated thank-you page in Astro at src/pages/thank-you.astro. Use this to confirm the message was sent, suggest next steps, or link back to your homepage.
If you're using the AJAX approach above, the redirect field is ignored because fetch() doesn't follow redirects in the same way. The JavaScript handles the success state directly.
Honeypot spam protection
FormWit includes automatic spam filtering on every form. The honeypot field adds a second layer of defense:
<input type="text" name="_gotcha" style="display:none" tabindex="-1" autocomplete="off" /> Bots that scrape pages and auto-fill every visible input will fill this hidden field. FormWit automatically rejects any submission where _gotcha has a value. Real visitors never see or interact with it.
For sites that get heavy bot traffic, you can also add hCaptcha, reCAPTCHA, or Cloudflare Turnstile. See the spam protection guide for setup instructions.
What you get with FormWit
- Email notifications for every submission
- Dashboard to view and manage submissions
- Built-in spam protection (honeypot + rate limiting + automatic filtering)
- Custom redirect URL after submission
- AJAX / fetch support for no-reload submissions
- File uploads, webhooks, and CSV export on paid plans
- 100 free submissions per month, unlimited forms
Summary
Adding a contact form to an Astro site takes about 5 minutes. Create an Astro component with a standard HTML form, point the action attribute at your FormWit endpoint, and deploy. No API routes, no server-side code, no client-side framework needed. The form works in static, SSR, and hybrid mode. Add the JavaScript snippet for progressive enhancement if you want inline success messages instead of a page redirect.
Get started free. Unlimited forms, 100 submissions/month, no credit card required.
Related guides: Static site contact form · HTML contact form · Vercel contact form · Next.js contact form · Spam protection methods · Contact form templates
Frequently asked questions
Does FormWit work with Astro SSR?
Yes. The form submits directly from the browser to FormWit's endpoint, so it works identically in static, SSR, and hybrid mode. Astro's rendering mode has no effect on form submission because the POST request bypasses your Astro server entirely.
Can I use Tailwind to style the form?
Yes. The form uses standard HTML elements, so Tailwind utility classes work directly on the inputs, labels, and buttons. Astro processes Tailwind at build time, so there is no runtime cost. See the Tailwind contact form guide for a copy-paste example.
Do I need client-side JavaScript?
No. The form works with zero JavaScript using native browser form submission. The browser sends a POST request and redirects to a thank-you page. Add JavaScript only if you want inline success messages without a page reload.
Want to skip the setup?
FormWit gives you a form endpoint in 60 seconds. Free plan, no credit card.
Need a form fast?
Build one visually with our free HTML form generator — no coding required.
Try the Form Generator →Add FormWit to your Astro site
Add a contact form to your site in 30 seconds. No backend code required.
Try FormWit Free