Adding a contact form to an Eleventy site
July 24, 2026
Eleventy people are particular about shipping plain HTML. It's half the reason you picked 11ty in the first place. Turns out a contact form is the easiest place in your whole site to hold that line: no client JavaScript, no serverless function bolted onto your static site, no build plugin.
1. Grab an endpoint
Spin up a NormForms form. Free tier,
no card required. You'll get a URL that looks like https://normforms.com/f/x7Kp2mQ9RtLw.
2. Drop the form into a template or include
It works the same in Nunjucks, Liquid, WebC, or plain HTML files. It's
just markup. Here it is as an include (_includes/contact-form.njk):
<form action="https://normforms.com/f/YOUR_TOKEN" method="POST"> <label for="nf-name">Name</label> <input id="nf-name" type="text" name="name" autocomplete="name" required> <label for="nf-email">Email</label> <input id="nf-email" type="email" name="email" autocomplete="email" required> <label for="nf-message">Message</label> <textarea id="nf-message" name="message" required></textarea> <input type="text" name="_gotcha" style="display:none" tabindex="-1" autocomplete="off"> <button type="submit">Send</button> </form>
{% include "contact-form.njk" %}
Want more fields? Add inputs: <input name="budget">,
a select, whatever you need. Any field name you send shows up in the
notification email, no configuration required. The _gotcha
honeypot stays hidden and empty. It's doing more anti-spam work than it
looks like.
3. Test it from localhost
Submit from npx eleventy --serve and you're automatically in
test mode. The email shows up with a [Test] prefix and
doesn't touch your quota. Set your production domain in the form settings
and submissions from any other origin get silently dropped.
Scorecard
Client JavaScript added: 0 bytes. Build time impact: none. Spam filtering, delivery, storage, and a dashboard: included. Your 11ty site stays a folder of honest HTML files. Now it has a working front door.