splitforms.com
ELEVENTY · CONTACT FORM

Contact form for Eleventy (11ty) websites

11ty's beauty is its simplicity — and a contact form should match. One Nunjucks (or Liquid) include, one form tag, no serverless function in sight. Pull the access key from global data, render zero JavaScript, and ship submissions straight to your inbox.

1,000 free submissions every month.·No credit card.
contact.htmlhtml18 lines
01{#
02 src/_includes/partials/contact-form.njk
03 Use in any template with: {% include "partials/contact-form.njk" %}
04 Set ACCESS_KEY in .env and expose it via eleventy.config.js (addGlobalData)
05#}
06
07<form action="https://splitforms.com/api/submit" method="POST">
08 <input type="hidden" name="access_key" value="{{ splitformsKey }}" />
09 <input type="hidden" name="redirect" value="{{ '/thanks/' | url }}" />
10
11 <input type="text" name="name" placeholder="Name" required />
12 <input type="email" name="email" placeholder="Email" required />
13 <textarea name="message" placeholder="Message" required></textarea>
14
15 <input type="checkbox" name="botcheck" style="display:none" tabindex="-1" />
16
17 <button type="submit">Send</button>
18</form>
1,000
submissions / mo, free
14ms
median latency, edge
0
lines of backend code
17+
frameworks supported
✶ Live preview

What your Eleventy contact form actually looks like.

Drop-in form backend with spam filtering, signed webhooks, and a real submissions dashboard. The same code in this preview is what you copy into your Eleventy project — no SDK, no plugin, no PHP.

  • 1,000 submissions per month, free forever
  • Honeypot + AI spam classifier on every plan
  • Signed webhooks to Slack, Discord, your server
Eleventy contact form on Splitforms — drop-in form backend with spam filtering and webhooks
§ 01Setup3 steps · 60 seconds · zero config

Ship a Eleventy contact form without a backend.

No SDK, no PHP, no plugin. Your form posts standard FormData to one URL — submissions land in your inbox.

STEP 01GENERATE

Get your free access key

Verify your email and your access key is generated instantly. Free for 1,000 submissions per month, forever.

Create your form

By signing up, you agree to our terms and privacy policy.

STEP 02EMBED

Drop in the Eleventy code

Copy the Eleventy snippet on the right and paste it into your project. Replace YOUR_ACCESS_KEY with the key from step 1.

snippethtml
{#
…
STEP 03RECEIVE

Submissions land in your inbox

Hits your dashboard and email in seconds. Forward to Slack, Discord, Sheets, Notion, or any signed webhook URL.

inbox · 1 newjust now
FROM contact@yoursite.com
New Eleventy form submission
Maya Iyer maya@studio71.co
Loved the new pricing page — quick question about the 4-year plan. Are usage limits per project or account-wide?
§ 02Live demosandboxed · no key required · no submission sent

Try it now — no signup, no key.

This is a styled HTML preview of what your Eleventy form will look like. Submitting opens a confirmation, no real request is sent.

preview · eleventylocalhost:3000
✦ what just happened

Your Eleventy form posts FormData to /api/submit. Splitforms validates the access key, runs the spam classifier, and forwards the parsed submission to your inbox plus the dashboard.

  • 14ms median round-trip from the edge.
  • Honeypot + classifier, no CAPTCHA.
  • Per-domain key locking out of the box.
REQUEST · POST /api/submit
{
  "access_key": "sk_live_4f9a_••••",
  "name":       "Maya Iyer",
  "email":      "maya@studio71.co",
  "message":    "…"
}
← 200 OK · { "success": true } · 14ms
§ 03Best practices5 rules · production-tested

How to ship this without regrets.

Five rules that make the difference between a form that works in the demo and a form that survives launch traffic.

  1. 01

    Define the key in `_data/site.js` (not site.json), reading from `process.env.SPLITFORMS_KEY`. Add a `.env` file to gitignore. Local dev and production both read from environment.

  2. 02

    Use a Nunjucks include (`{% include "partials/contact-form.njk" %}`) so the form is reusable across templates without copy-paste.

  3. 03

    Build the /thanks page as a normal Eleventy content file (`content/thanks.md`) so it inherits your layout, header, and footer.

  4. 04

    Lock the access key to your production domain in the splitforms dashboard — Eleventy's local dev server (port 8080) needs a separate dev key or be added to allowed domains.

  5. 05

    If your site uses 11ty Image or 11ty Bundle plugins, the contact form template still works as plain HTML — no plugin compatibility issues.

§ 04Common gotchas in Eleventy6 edge cases worth knowing

What bites people who skip the docs.

Worth a 60-second skim before you ship to production. Each one has caused a Eleventy support ticket at least once.

⚠ gotcha

Eleventy v3 is ESM-only — your config must be .mjs

If you upgraded to Eleventy 3 and your .eleventy.js (CommonJS) silently stopped exposing globals, that's why. Rename to eleventy.config.mjs and use ESM export default. Otherwise addGlobalData('splitformsKey', …) won't reach your templates.

⚠ gotcha

Liquid filters and Nunjucks filters have different names

{{ '/thanks/' | url }} works in Nunjucks (with the eleventy-plugin-url plugin). In Liquid, the filter is | url_for or you skip the filter entirely and write the path literally. Mismatched filter names render nothing — the form's redirect URL becomes empty.

⚠ gotcha

Global data with sensitive values gets committed by accident

If you put your access key in _data/site.json (the obvious place), it ships to your repo. Use _data/site.js and read from process.env.SPLITFORMS_KEY instead — then add .env to gitignore. Eleventy auto-loads .env if you have dotenv installed.

⚠ gotcha

Permalinks: false on the contact page makes the redirect fail

If your contact page has permalink: false (rare but possible), it's not built — and splitforms's redirect target points at a 404. Always ensure both the form page and the /thanks page have valid permalinks.

⚠ gotcha

Pagination data clobbers your `splitformsKey` global

If you reuse the variable name splitformsKey inside a paginated template's data, Eleventy's data cascade has paginated data win over global data. Use a unique-enough name like siteSplitformsKey to avoid the conflict.

⚠ gotcha

Eleventy serverless (`eleventy-plugin-serverless`) bundles env vars at build, not request time

If you use eleventy-plugin-serverless to render the contact page on-demand via Netlify Functions, you might assume process.env.SPLITFORMS_KEY is read per-request. It isn't — Eleventy serverless bundles the rendered template into a Netlify Function during build, capturing the env vars from your build environment. Rotating the key in Netlify's dashboard does nothing until the next deploy. Fix: read the key inside the form template via <%= process.env.SPLITFORMS_KEY %> rendered at request time using a Nunjucks {% raw %} escape, or skip serverless rendering for the contact page.

§ 04bNative Eleventy forms…and where they break down

How Eleventy handles forms without splitforms.

The shape of the problem before splitforms enters the picture — and the gap it fills for Eleventy specifically.

Eleventy is a Node-based static site generator — it produces HTML at build time and ships nothing else. There is no runtime, no /api/contact endpoint, no hooks for handling a form POST. The historical workarounds: (a) deploy to Netlify and use Netlify Forms, (b) write a Cloudflare Worker that handles POST /contact and forwards to your email provider (~4 hours plus ongoing operation), or (c) use a third-party form API (Formspree, Web3Forms, Basin, splitforms). Eleventy's data cascade lets you neatly pull an access key from _data/site.js and use it in a Nunjucks/Liquid include — but the actual delivery layer is always external. Splitforms is the lowest-friction external option.

§ 04cAlternative integration patterns2 ways to wire it

Two ways to ship splitforms on Eleventy.

Pick the pattern that matches your constraints — JS budget, key-exposure tolerance, server-side opacity. Both produce the same result.

PATTERN A

Pattern A — Nunjucks include from `_includes/partials/`

Save as src/_includes/partials/contact-form.njk. Use from any template with {% include "partials/contact-form.njk" %}. Pulls the key from a global data file that reads process.env.SPLITFORMS_KEY.

pattern-a.htmlhtml7 lines
01<form action="https://splitforms.com/api/submit" method="POST">
02 <input type="hidden" name="access_key" value="{{ splitformsKey }}" />
03 <input type="hidden" name="redirect" value="{{ '/thanks/' | url }}" />
04 <input name="email" type="email" required />
05 <textarea name="message" required></textarea>
06 <button type="submit">Send</button>
07</form>
PATTERN B

Pattern B — Eleventy v3 ESM config

Eleventy 3 is ESM-only. Use eleventy.config.mjs and addGlobalData to wire the key from environment. Liquid templates work the same way — just save as .liquid.

pattern-b.htmlhtml6 lines
01// eleventy.config.mjs
02import "dotenv/config";
03export default function (config) {
04 config.addGlobalData("splitformsKey", process.env.SPLITFORMS_KEY);
05 return { dir: { input: "src", output: "_site" } };
06}
§ 04dDeployment notes for Eleventyhosting · env vars · CSP

Shipping Eleventy + splitforms to production.

Host-specific gotchas, env-var conventions, and the boring-but-load-bearing details for putting this on the public internet.

Eleventy deploys to any static host. The form posts cross-origin to splitforms.com so the host is irrelevant for delivery. Eleventy v3's ESM-only config is the major upgrade gotcha — .eleventy.js (CommonJS) silently stops exposing globals; rename to eleventy.config.mjs. Local dev runs on localhost:8080 — add to splitforms allowed-domains for testing or use a separate dev key. _data/site.js (not .json) lets you read from process.env; keep .env in gitignore. For 11ty + Netlify (the classic combo), you can use Netlify Forms instead, but you cap out at 100/mo — splitforms gives 1,000/mo and works on every host.

§ 05Comparisonvs native eleventy

splitforms vs native eleventy.

What you get for free vs what you build, pay for, or do without.

FeatureNative Eleventysplitforms
Setup timeCloudflare Worker + email + spam (4 hours)60 seconds, one include
JavaScript shipped0 KB0 KB (pure HTML)
Spam filterDIYBuilt-in
Hosting costWorker requests count toward CF limitsFree (CDN + splitforms)
Submission storageWorker KV / external DBDashboard included
Eleventy v2 + v3 supportWorker code is the sameSame include, both versions
§ 07Questions6 answered

Things developers ask before they integrate.

Direct answers, no marketing fluff. Missing one? Email hello@splitforms.com.

01How do I add a contact form to Eleventy?
Save the snippet as src/_includes/partials/contact-form.njk. Add splitformsKey to your global data (in _data/site.js, reading from process.env.SPLITFORMS_KEY). Then {% include "partials/contact-form.njk" %} from any template.
02Does splitforms work with Eleventy v2 and v3?
Yes — both. The form template is plain HTML with Nunjucks/Liquid syntax that hasn't changed across versions. The only v3 gotcha is that your config file must be ESM (.mjs).
03How do I handle form errors in 11ty?
Eleventy is static — it can't render dynamic error responses. Two options: (1) use the pure-HTML form and let splitforms render its default error page (configurable in dashboard), or (2) add a small inline <script> that intercepts submit and renders inline errors (see the AJAX page).
04Can I use splitforms with Eleventy and Liquid templates?
Yes. The snippet is in Nunjucks but the same logic in Liquid is one-line different: {% include 'partials/contact-form.liquid' %} and use {{ splitformsKey }} directly (Liquid doesn't need the URL filter). Save the file with .liquid extension.
05How do I customize the success / redirect behavior?
Set the hidden redirect field's value to your /thanks page URL: {{ '/thanks/' | url }} (Nunjucks). Build a /thanks Markdown file with your normal layout. Splitforms 302s on success.
06Will this work with 11ty serverless / Eleventy on Netlify?
Yes. The form posts to splitforms.com directly from the static HTML, so it works regardless of whether the page itself was built statically or by Netlify's on-demand builders. No Netlify Function code required.
✻ ✻ ✻

Ship your Eleventy contact form in 60 seconds.

1,000 free submissions per month. No credit card. Lock the access key to your domains, paste the snippet, watch submissions land in your inbox.

Get free access key →Read the docs
v0.1 · founders pricing locked in · early access open