How to Add a Contact Form to Eleventy (11ty)

Last updated: March 2026

Eleventy (11ty) is a JavaScript-based static site generator known for its simplicity and flexibility. It supports multiple template languages (Nunjucks, Liquid, Markdown, EJS, and more) and outputs plain HTML with zero client-side JavaScript by default. That simplicity is one of its biggest strengths, but it also means there's no built-in way to handle form submissions. Your Eleventy site doesn't have a server, so when a visitor fills out a contact form, there's nowhere for the data to go unless you set up an external backend.

This guide walks you through adding a fully functional contact form to an Eleventy site using FormWit as the form backend. No build plugins, no serverless functions, no Node.js server. Just an HTML form that sends data to an endpoint.

How it works

Eleventy generates static HTML files at build time. Your site is just HTML, CSS, and optionally some JavaScript. No server process running in production. When a visitor submits your contact form, the browser sends the form data directly to FormWit's endpoint via a standard POST request. FormWit validates the data, checks for spam, stores the submission in your dashboard, and sends you an email notification.

The complete flow:

  1. A visitor fills out the contact form on your Eleventy site
  2. The browser sends the form data to your FormWit endpoint
  3. FormWit validates the data and filters out spam
  4. The submission is stored in your dashboard and you receive an email notification
  5. The visitor is redirected to a thank-you page or sees a success message

This works because the form submission happens entirely in the browser at runtime. It has nothing to do with Eleventy's build process or template rendering.

Set up an Eleventy contact form

Step 1: Create a FormWit account

Go to app.formwit.com/auth/signup and create a free account. No credit card required. Once signed in, click Create Form and give it a name (e.g., "Eleventy Contact Form"). You'll receive a unique endpoint URL that looks like https://app.formwit.com/api/s/YOUR_FORM_ID. Copy this. You'll need it in the next step.

Step 2: Create the contact page

Eleventy supports many template languages, so you can create your contact page in whichever format you prefer. Here are examples for the most common ones.

Using Nunjucks (contact.njk)

Create a file called contact.njk in your Eleventy project's source directory:

---
title: Contact Us
layout: base.njk
---

<h1>Contact Us</h1>
<p>Have a question or want to get in touch? Fill out the form below and we'll get back to you.</p>

<form
  action="https://app.formwit.com/api/s/YOUR_FORM_ID"
  method="POST"
>
  <div>
    <label for="name">Name</label>
    <input type="text" id="name" name="name" required />
  </div>

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

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

  <!-- Honeypot spam protection -->
  <input type="text" name="_gotcha" style="display:none" tabindex="-1" autocomplete="off" />

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

Using Liquid (contact.liquid)

If you prefer Liquid templates, the form markup is identical. Create a file called contact.liquid:

---
title: Contact Us
layout: base.liquid
---

<h1>Contact Us</h1>
<p>Have a question? Send us a message and we'll reply as soon as we can.</p>

<form
  action="https://app.formwit.com/api/s/YOUR_FORM_ID"
  method="POST"
>
  <div>
    <label for="name">Name</label>
    <input type="text" id="name" name="name" required />
  </div>

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

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

  <input type="text" name="_gotcha" style="display:none" tabindex="-1" autocomplete="off" />

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

Replace YOUR_FORM_ID with the endpoint ID from your FormWit dashboard. The form uses a standard HTML action attribute to POST data to the FormWit endpoint. No JavaScript is required for this to work. The browser handles it natively.

Step 3: Add honeypot spam protection

The hidden _gotcha field in the examples above is a honeypot. It's invisible to real visitors (hidden with display: none), but spam bots fill in every field they find. When FormWit receives a submission with the _gotcha field filled in, it silently discards it as spam. No CAPTCHAs, no friction for your visitors.

The key attributes that make the honeypot work:

  • style="display:none" hides the field visually
  • tabindex="-1" prevents keyboard users from accidentally tabbing into it
  • autocomplete="off" prevents browser autofill from filling it in

For a deeper look at spam prevention methods, see the spam protection guide.

Step 4: Add AJAX submission (optional)

The basic HTML form above works perfectly. It submits the data and redirects the visitor. But if you want a smoother experience without a page reload, you can add a small JavaScript snippet. Create this as an inline script at the bottom of your contact page or as a separate JS file:

<script>
  const form = document.querySelector('form');

  form.addEventListener('submit', async function (e) {
    e.preventDefault();
    const button = form.querySelector('button[type="submit"]');
    button.textContent = 'Sending...';
    button.disabled = true;

    try {
      const response = await fetch(form.action, {
        method: 'POST',
        body: new FormData(form),
      });

      if (response.ok) {
        form.reset();
        button.textContent = 'Send Message';
        button.disabled = false;

        // Show success message
        const msg = document.createElement('p');
        msg.textContent = 'Message sent successfully!';
        msg.style.color = '#16a34a';
        form.appendChild(msg);
      } else {
        throw new Error('Submission failed');
      }
    } catch {
      button.textContent = 'Send Message';
      button.disabled = false;

      const msg = document.createElement('p');
      msg.textContent = 'Something went wrong. Please try again.';
      msg.style.color = '#dc2626';
      form.appendChild(msg);
    }
  });
</script>

This uses vanilla JavaScript. No frameworks or libraries needed. It intercepts the form's submit event, sends the data with fetch, and displays a success or error message inline. Eleventy doesn't bundle or process JavaScript by default, so this script runs as-is in the browser.

Eleventy-specific details

Works with every template language

Eleventy supports Nunjucks, Liquid, Markdown, EJS, Handlebars, Pug, and plain HTML. Because the contact form is just standard HTML markup, it works in any of them. The form doesn't use any template-specific features. It's pure HTML that Eleventy passes through unchanged during the build. Pick whichever template language you're already using for the rest of your site.

No plugins required

Some form solutions require Eleventy plugins or build-time configuration. FormWit doesn't. There's nothing to add to your .eleventy.js (or eleventy.config.js) configuration file. The form works with a plain HTML form element and a POST to an external URL. This means fewer dependencies, no plugin version conflicts, and nothing that can break when you upgrade Eleventy.

Works with Eleventy Serverless (but doesn't need it)

Eleventy Serverless lets you run Eleventy templates in a serverless function at request time instead of at build time. If you're using Eleventy Serverless, the contact form still works exactly the same way. The form submission happens in the visitor's browser, not during template rendering, so it's completely independent of whether your page is built statically or rendered on demand.

Compatible with any hosting

Because Eleventy produces plain static files and the form submission goes directly from the browser to FormWit, your site can be deployed anywhere: Netlify, Vercel, Cloudflare Pages, GitHub Pages, Amazon S3, or any other static hosting. No special hosting features or serverless function support is required.

Using Eleventy data files

If you want to keep your FormWit endpoint URL in a centralized data file instead of hardcoding it, you can use Eleventy's global data directory. Create a file at _data/formwit.json:

{
  "endpoint": "https://app.formwit.com/api/s/YOUR_FORM_ID"
}

Then reference it in your Nunjucks template:

<form action="{{ formwit.endpoint }}" method="POST">
  <!-- form fields -->
</form>

Or in Liquid:

<form action="{{ formwit.endpoint }}" method="POST">
  <!-- form fields -->
</form>

This keeps your endpoint URL in one place and makes it easy to change later without editing every template that uses it.

Adding a custom redirect

By default, the basic HTML form will redirect to a FormWit confirmation page after submission. If you want to redirect visitors to your own thank-you page instead, add a hidden redirect_to field:

<input type="hidden" name="redirect_to" value="https://yoursite.com/thank-you/" />

You can create a thank-you.njk or thank-you.md page in your Eleventy project for this. The redirect_to value must be a full URL starting with http or https.

If you're using the AJAX approach from Step 4, the redirect is handled in JavaScript instead:

if (response.ok) {
  window.location.href = '/thank-you/';
}

Styling the form

Eleventy doesn't prescribe a CSS approach, so your form works with whatever styling method you use: plain CSS, Sass, PostCSS, Tailwind CSS, or any other tool. Here's a basic CSS example you can include in your site's stylesheet:

.contact-form {
  max-width: 500px;
}

.contact-form div {
  margin-bottom: 1rem;
}

.contact-form label {
  display: block;
  margin-bottom: 0.25rem;
  font-weight: 600;
}

.contact-form input,
.contact-form textarea {
  width: 100%;
  padding: 0.5rem;
  border: 1px solid #ccc;
  border-radius: 4px;
}

.contact-form button {
  background: #4f46e5;
  color: white;
  padding: 0.5rem 1.5rem;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}

.contact-form button:hover {
  background: #4338ca;
}

.contact-form button:disabled {
  opacity: 0.6;
  cursor: not-allowed;
}

Add a class="contact-form" attribute to your <form> element to apply these styles.

Why not use a serverless function?

If you deploy to Netlify or Vercel, you could write a serverless function to handle form submissions yourself. But that means:

  • Writing the email sending logic with an email API like SendGrid or Resend
  • Managing API keys and environment variables
  • Building error handling and retry logic
  • No built-in submission dashboard or history
  • No built-in spam protection
  • Your form only works on hosts that support serverless functions

A form backend handles all of this out of the box. Your Eleventy site stays fully static and portable. No vendor lock-in to a specific hosting platform.

Summary

Eleventy generates static HTML, which means there's no server to handle form submissions. With FormWit, you add a standard HTML form to any template (Nunjucks, Liquid, Markdown, or any other format Eleventy supports), point the action to your endpoint, and get email notifications, submission storage, and spam protection without any plugins, serverless functions, or server-side code.

FormWit's free plan includes unlimited forms, 100 submissions per month, email notifications, and built-in spam protection. Create your free account and add a contact form to your Eleventy site in minutes.

Related guides: HTML contact form · Hugo contact form · Jekyll contact form · Spam protection · Contact form templates

Frequently asked questions

Does FormWit work with Eleventy?

Yes. Eleventy generates static HTML files, and FormWit works with any static site. You add a standard HTML form to any Eleventy template (Nunjucks, Liquid, Markdown, or plain HTML), point the action to your FormWit endpoint, and submissions are processed at runtime in the browser.

Can I use Nunjucks templates?

Yes. The form is plain HTML, so it works in any Eleventy template language. In Nunjucks, you can reference the endpoint URL from a global data file using double-brace syntax, keeping your templates clean and the endpoint configurable from one place.

Does the form work on Netlify?

Yes. Because the form posts directly to FormWit's external endpoint, it works on Netlify, Vercel, Cloudflare Pages, GitHub Pages, and any other static host. No special hosting features or build plugins are required.

Want to skip the setup?

FormWit gives you a form endpoint in 60 seconds. Free plan, no credit card.

Create Free Form

Need a form fast?

Build one visually with our free HTML form generator — no coding required.

Try the Form Generator →

Add FormWit to your Eleventy site

Add a contact form to your site in 30 seconds. No backend code required.

Try FormWit Free