Bitdoze Logo
8 min read

How To Add A Contact Form To Astro (Free, No Backend)

Learn how to add a working contact form to your Astro website that sends email notifications. Three options: Web3Forms, formsubmit.co, and OpnForm.

How To Add A Contact Form To Astro (Free, No Backend)

Static sites can’t process form submissions on their own. There’s no server to handle the POST request, validate the data, or send an email. You need an external service to act as the backend.

This guide covers three ways to add a working contact form to Astro that sends you email notifications when someone submits it. The first option (Web3Forms) is the one I recommend for most people.

Other Astro tutorials:

Web3Forms is a form-to-email API. You create a form in HTML, point it at their endpoint, and submissions land in your inbox. It works without a backend server or any server-side code.

Free plan limits: 250 submissions/month, honeypot spam protection, email notifications included, 30-day submission storage. No credit card required. File uploads are available on the paid Starter plan ($12/mo).

Step 1: Get an access key

Go to web3forms.com and enter your email address. You’ll receive an access key. This key is not a secret — it’s safe to put in client-side code. It acts as an alias for your email address.

Step 2: Create the form component

Create a new Astro component or add the form to an existing page. Here’s a minimal example:

---
// src/pages/contact.astro
import Layout from "../layouts/Layout.astro";
---

<Layout title="Contact">
  <form
    action="https://api.web3forms.com/submit"
    method="POST"
    id="contact-form"
    data-astro-reload
    novalidate
  >
    <input type="hidden" name="access_key" value="YOUR_ACCESS_KEY_HERE" />

    <!-- Honeypot spam protection -->
    <input type="checkbox" name="botcheck" style="display: none;" />

    <div>
      <label for="name">Name</label>
      <input type="text" name="name" id="name" required />
    </div>

    <div>
      <label for="email">Email</label>
      <input type="email" name="email" id="email" required />
    </div>

    <div>
      <label for="message">Message</label>
      <textarea name="message" id="message" rows="5" required></textarea>
    </div>

    <button type="submit">Send</button>

    <p id="result"></p>
  </form>
</Layout>

Replace YOUR_ACCESS_KEY_HERE with the key from step 1.

The data-astro-reload attribute is important if you’re using Astro’s View Transitions. Without it, the form submission may fail because Astro intercepts the navigation.

Step 3: Add AJAX submission (optional but better UX)

The plain HTML form works, but it redirects the user to a Web3Forms success page. If you want to show a “sent successfully” message without leaving the page, add this script:

<script is:inline>
  document.addEventListener("DOMContentLoaded", () => {
    const form = document.getElementById("contact-form");
    const result = document.getElementById("result");

    form.addEventListener("submit", function (e) {
      e.preventDefault();
      const formData = new FormData(form);
      const object = Object.fromEntries(formData);
      const json = JSON.stringify(object);

      result.textContent = "Sending...";

      fetch("https://api.web3forms.com/submit", {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          Accept: "application/json",
        },
        body: json,
      })
        .then(async (response) => {
          const data = await response.json();
          if (response.status === 200) {
            result.textContent = data.message;
          } else {
            result.textContent = data.message;
          }
        })
        .catch(() => {
          result.textContent = "Something went wrong.";
        })
        .then(() => {
          form.reset();
        });
    });
  });
</script>

If you use View Transitions in Astro, replace the DOMContentLoaded listener with astro:page-load. Here’s the complete version:

<script is:inline>
  document.addEventListener("astro:page-load", () => {
    const form = document.getElementById("contact-form");
    const result = document.getElementById("result");

    if (!form) return;

    form.addEventListener("submit", function (e) {
      e.preventDefault();
      const formData = new FormData(form);
      const object = Object.fromEntries(formData);
      const json = JSON.stringify(object);

      result.textContent = "Sending...";

      fetch("https://api.web3forms.com/submit", {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          Accept: "application/json",
        },
        body: json,
      })
        .then(async (response) => {
          const data = await response.json();
          if (response.status === 200) {
            result.textContent = data.message;
          } else {
            result.textContent = data.message;
          }
        })
        .catch(() => {
          result.textContent = "Something went wrong.";
        })
        .then(() => {
          form.reset();
        });
    });
  });
</script>

The if (!form) return; guard prevents errors on pages that don’t have the contact form. With View Transitions, Astro re-runs the astro:page-load event on every navigation, so the check matters.

Step 4: Add a redirect after submission (optional)

Instead of AJAX, you can redirect to a thank-you page after submission. Add this hidden input to your form:

<input type="hidden" name="redirect" value="https://yourdomain.com/thanks" />

Web3Forms will redirect the user there after processing the submission.

Step 5: Style the form

The form markup above is bare HTML. Style it with your preferred approach — Tailwind, vanilla CSS, or a component library. Web3Forms is just an API endpoint. It doesn’t inject any UI or styles.

The Web3Forms Astro docs have a complete Tailwind-styled example if you want a starting point.

Option 2: formsubmit.co

Formsubmit.co is another free form-to-email service. No signup required. You point your form’s action attribute at their endpoint with your email address, submit the form once, click a confirmation link, and you’re done.

Setup

<form
  action="https://formsubmit.co/[email protected]"
  method="POST"
  data-astro-reload
>
  <input type="text" name="name" required />
  <input type="email" name="email" required />
  <textarea name="message" required></textarea>
  <button type="submit">Send</button>
</form>

Submit the form once. Check your inbox for a confirmation email from Formsubmit.co. Click the link. From that point on, all submissions go to your email.

Useful hidden fields

<!-- Redirect after submission -->
<input type="hidden" name="_next" value="https://yourdomain.com/thanks" />

<!-- CC another email -->
<input type="hidden" name="_cc" value="[email protected]" />

<!-- Set subject line -->
<input type="hidden" name="_subject" value="New contact form submission" />

<!-- Enable reCAPTCHA -->
<input type="hidden" name="_captcha" value="true" />

formsubmit.co vs Web3Forms

Feature formsubmit.co Web3Forms
Free submissions Unlimited 250/month
Signup required No (email confirmation) Yes (email only)
Spam protection reCAPTCHA (free) Honeypot (free), reCAPTCHA (paid)
AJAX support Yes Yes
Custom redirect Yes Yes
File uploads Yes (free) Paid only (Starter $12/mo)
Submission storage No 30 days (free)

Both work well. formsubmit.co has no monthly cap and supports file uploads on the free plan, but Web3Forms has a cleaner API and stores submissions for 30 days, which is useful if an email gets lost.

Option 3: OpnForm (embedded form builder)

OpnForm is an open-source form builder with a drag-and-drop editor. You design the form visually, then embed it as an iframe.

What’s free vs. paid: OpnForm’s free plan includes basic email notifications through their built-in email integration. However, using your own SMTP server (custom SMTP), Slack/Discord/Telegram notifications, and Captcha are all Pro plan features ($25/mo). If you’re fine with OpnForm sending the emails through their default service, the free plan works for basic contact forms. If you need custom SMTP or anti-spam Captcha, you’ll need to upgrade.

How to use OpnForm in Astro

  1. Create an account at opnform.com and build your form.
  2. Go to the form’s Share page and copy the embed code.
  3. Paste it into your Astro page:
---
// src/pages/contact.astro
import Layout from "../layouts/Layout.astro";
---

<Layout title="Contact">
  <iframe
    style="border:none;width:100%;"
    height="500px"
    src="https://opnform.com/forms/your-form-slug"
  ></iframe>
</Layout>

Adjust the height value to fit your form. The iframe approach means you don’t have control over the form’s styling — OpnForm handles that in the embed.

OpnForm form builder settings

Which option should you pick?

Just want emails in your inbox for free? Use Web3Forms. It takes 5 minutes, the free plan handles 250 submissions/month (more than enough for a contact form), and the honeypot spam protection works out of the box.

Need unlimited submissions and file uploads without creating an account? Use formsubmit.co. The one-time email confirmation is the only setup step.

Want a visual form builder with advanced logic and conditional fields? Use OpnForm. The free plan includes basic email notifications, but you’ll need the Pro plan for custom SMTP and Captcha.

For a simple contact form that sends you an email, Web3Forms is the best balance of simplicity and features. Here’s the minimal code again:

<form action="https://api.web3forms.com/submit" method="POST" data-astro-reload>
  <input type="hidden" name="access_key" value="YOUR_KEY" />
  <input type="checkbox" name="botcheck" style="display: none;" />
  <input type="text" name="name" placeholder="Name" required />
  <input type="email" name="email" placeholder="Email" required />
  <textarea name="message" placeholder="Message" required></textarea>
  <button type="submit">Send</button>
</form>

Get your free access key at web3forms.com and you’re done.