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
- Plain HTML form — works on any static Astro site, zero JavaScript
- Client-side fetch — stay-on-page UX with a loading state
- 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
- Sign up at dashboard.formboost.app — free, no credit card
- Create a new endpoint
- Copy the URL and replace
YOUR_ENDPOINT_IDin the examples above