An Astro contact form that works on a static site
Paste one component and every message reaches your inbox, while your Astro site stays fully static. Messages are stored in the EU, and spam is filtered out before it reaches you.
14 days free, no card. Paste the address where the code says YOUR_FORM_ID.
---
// src/components/Contact.astro
const endpoint = 'https://formhaven.net/f/YOUR_FORM_ID';
---
<form action=https://formhaven.net/f/YOUR_FORM_ID method="POST">
<label>Your name <input type="text" name="name" required /></label>
<label>Your email <input type="email" name="email" required /></label>
<label>Message <textarea name="message" required></textarea></label>
<input type="text" name="_gotcha" style="display:none" tabindex="-1" autocomplete="off" />
<input type="hidden" name="_next" value="/thanks" />
<button type="submit">Send message</button>
</form>
Set it up
-
1
Create a form in FormHaven
Sign up, press New form and copy its address. It looks like https://formhaven.net/f/YOUR_FORM_ID.
-
2
Add the component
Save the code above as src/components/Contact.astro, paste your address into endpoint, and put <Contact /> on your contact page.
-
3
Add a thank-you page
Create src/pages/thanks.astro. The hidden _next field sends visitors there after they press Send. Leave it out and they see our thank-you page instead.
-
4
Send a test
Run astro dev, fill in the form, and check your inbox. Or press Send a test submission in the dashboard.
Keep the visitor on the page
The visitor sees the answer under the button and never leaves the page. Astro bundles the <script> and runs it in the browser. Without JavaScript the form still posts the normal way.
<form id="contact" action=https://formhaven.net/f/YOUR_FORM_ID method="POST">
<!-- the same fields as above -->
</form>
<p id="contact-status" role="status"></p>
<script>
const form = document.getElementById('contact') as HTMLFormElement;
const status = document.getElementById('contact-status')!;
form.addEventListener('submit', async (event) => {
event.preventDefault();
const response = await fetch(form.action, {
method: 'POST',
headers: { Accept: 'application/json' },
body: new FormData(form),
});
const { ok, message } = await response.json();
status.textContent = ok ? 'Thanks, your message is on its way.' : message;
if (ok) form.reset();
});
</script>
Stop bots that skip the hidden field
Turn on the bot check in the form's settings and add this line once to the page. Visitors see nothing, and it sets no cookies. The fetch version above sends it too, because new FormData(form) includes the field it fills in.
<script src="https://formhaven.net/bot-check.js" async></script>
Questions about this setup
- Do I need an adapter or output: 'server'?
- No. The form posts to FormHaven, not to your Astro site, so the default static output works. Astro's own form recipe and Actions need on-demand rendering, which means an adapter and a server to run it.
- Which hosts does this work on?
- Any host that serves your built files: Netlify, Vercel, Cloudflare Pages, GitHub Pages or your own server. Nothing runs on the host when a visitor presses Send.
- How do I answer a message?
- Press Reply in the email. The address the visitor typed in the email field is set as the reply address.
- Can my client get the messages instead of me?
- Yes. Add up to 5 more addresses to the form. Each one confirms by a link first, and their copy carries no FormHaven branding. You can turn your own copy off.
- Where is the data stored?
- On our servers in the EU, for 1 to 365 days as you choose per form, or not at all: then each message is emailed and its answers deleted. A data processing agreement covers every account.
More to read
Get your form address in a minute
14 days on Agency limits. No card.