How to Add a Contact Form to an Astro Site (Static, No Server)

Tutorials4 min read
Astro contact form banner: Static, No Server

Astro ships almost no JavaScript by default. That is great for speed and worse for the old habit of bolting on a Node mailer or an SSR adapter just so a contact form can POST. This guide shows how to add a contact form to an Astro site with a static form API: plain HTML in a .astro component, no adapter required, and optional client JS only if you want inline success states.

The same pattern works on Cloudflare Pages, Netlify, Vercel, GitHub Pages, and any host that serves dist/.

What you’ll need

No SSR adapter for the form. No mailto:. Optional WhatsApp later on Pro or Agency.

Why Astro forms do not need a server

Approach Problem
SSR adapter + server endpoint Extra deploy surface for one form
mailto: Opens the visitor’s mail client; weak spam control
Client-only secrets Never put private mail-provider tokens in the browser
Form API Browser POSTs to a managed URL; site stays static

FormsReach is a form API: set action to https://api.formsreach.com/submit, include a hidden api_key, and notifications leave your app. Pillar guide: send form submissions to WhatsApp. Broader static-site notes: contact form on a static website without a backend.

Step 1: Create a ContactForm component

Prefer a small .astro component so every page reuses the same markup. Zero client JS:

---
// src/components/ContactForm.astro
---
<form action="https://api.formsreach.com/submit" method="POST">
  <input type="hidden" name="api_key" value="YOUR_API_KEY">
  <input type="text" name="name" required>
  <input type="email" name="email" required>
  <textarea name="message" required></textarea>

  <button type="submit">Submit Form</button>
</form>

Replace YOUR_API_KEY with the key from the dashboard. Keep field name attributes consistent with how you want notifications to read (name, email, message work out of the box).

Style with scoped CSS, Tailwind, or global classes. Astro does not need a framework island for this form.

Step 2: Use the component on a page

---
import ContactForm from '../components/ContactForm.astro';
---
<main>
  <h1>Contact</h1>
  <ContactForm />
</main>

Build and deploy as usual (astro build, then host dist/). No form-specific adapter.

Step 3: Domain allowlist and thank-you page

  1. Publish the site on HTTPS.
  2. In FormsReach, add the live host under Allowed domains.
  3. Optionally set a redirect URL to a static /thanks/ page in the same Astro project.

Test on production, not only astro dev, so the allowlist matches the real origin.

Step 4: Env-friendly API key (optional)

For open-source repos, inject the key at build time instead of committing it:

---
// src/components/ContactForm.astro
const apiKey = import.meta.env.PUBLIC_FORMSREACH_KEY;
---
<form action="https://api.formsreach.com/submit" method="POST">
  <input type="hidden" name="api_key" value={apiKey}>
  <!-- fields unchanged -->
</form>

Set PUBLIC_FORMSREACH_KEY in your host’s env, and keep domain allowlisting enabled. The form key is public by design for HTML embeds; the allowlist is the main abuse control.

Step 5: Optional JS for inline success

If you want fetch submit without a full navigation, use the browser SDK on a page that opts into a script:

<script src="https://unpkg.com/@formsreach/js/dist/formsreach.min.js"></script>
<script>
  FormsReach.init({ apiKey: 'YOUR_API_KEY' });
</script>

<form data-formsreach>
  <input type="text" name="name" required />
  <input type="email" name="email" required />
  <textarea name="message" required></textarea>
  <button type="submit">Submit</button>
</form>

Load that only where you need it so the rest of the site stays zero-JS. React islands can use @formsreach/react instead: React contact form without a backend. For App Router Next, see Next.js contact form without API routes.

Step 6: Notifications (email first, WhatsApp when ready)

Free includes 500 credits/month. A typical path is 1 credit for an accepted submission and 1 credit for an email notification. Webhooks are 0 credits on every plan.

WhatsApp via the official Meta WhatsApp Business API is on Pro and Agency. Turn it on in the dashboard without changing your Astro markup.

Troubleshooting

Form reloads, nothing in the dashboard
Confirm action and method="POST", and that api_key is present in the built HTML. Check Network for api.formsreach.com.

Works locally, fails in production
Add the published host to Allowed domains. Confirm env-based keys were inlined at build time.

I added an adapter only for the form
You can remove it for this path. The form does not need SSR.

Want multi-framework snippets
Enable code tabs on this post layout or copy from the homepage CodeTabs; the HTML/Astro path above is the zero-JS default.

Keep Astro static; let a form API own delivery

An Astro contact form does not need a server of your own. One POST endpoint, spam filtering, email on Free (500 credits/month), free webhooks, and WhatsApp on Pro and Agency via Meta’s Business API. That preserves Astro’s static default while still shipping a real contact path.

Get your free API key →

          <form action="https://api.formsreach.com/submit" method="POST">
  <input type="hidden" name="api_key" value="YOUR_API_KEY">
  <input type="text" name="name" required>
  <input type="email" name="email" required>
  <textarea name="message" required></textarea>

  <button type="submit">Submit Form</button>
</form>
        
#astro#static-sites#no-backend