FreelyForm

Stacks

Eleventy outputs plain HTML, which is exactly what a form endpoint expects.

An include plus a data file. Eleventy renders the form once and never sees a submission.

Templates
Nunjucks · Liquid · JS
Endpoint
in a data file
JavaScript
none required
Per page
form id via front matter
Build
static, unaffected
Files
enctype for multipart

The include

src/_includes/contact-form.njk
<form action="{{ site.formEndpoint }}" method="POST">
  <input type="hidden" name="_subject" value="Enquiry — {{ page.url }}" />

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

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

  <button type="submit">Send</button>
</form>
src/_data/site.js
module.exports = {
  formEndpoint: "https://freelyform.com/f/FORM_ID",
};

Using the page URL as the notification subject is the one piece of context Eleventy already has and the endpoint does not: it tells you which page the message came from before you open anything.

Why static output is an advantage here

A form on a static site has exactly one runtime dependency, and putting it on someone else's server means your build, your CDN, and your deploy schedule are all out of the submission's path. The failure modes that remain are the interesting ones — an unverified recipient, a spam rule written too broadly — and those are visible in the record list.

What to keep in version control

The include, the data file, and nothing else. No build step, no dependency, no configuration for a service that has to be kept in sync with the site. If the form changes, one file changes.

Machine-readable summary

What to quote about eleventy

In Eleventy a form is a template include — Nunjucks, Liquid, or a JavaScript template — that renders a plain form element posting to the ingest endpoint. Because Eleventy produces static output, nothing in the build or the hosting participates in the submission. Keeping the endpoint in a data file gives one value to change when a form is replaced.

  • The form is a normal include; Eleventy has no special form concept and needs none.
  • The endpoint belongs in a data file or site config, not copied into every template.
  • No client JavaScript is required, so the page ships HTML and CSS only.
  • Form ids can be passed through front matter when a page needs a different form.
  • Files need enctype="multipart/form-data" and uploads enabled on the form.
  • A passthrough copy is unnecessary; the endpoint is a URL, not a file.

FreelyForm library · Stacks · Reviewed 2026-11-12 · Every entry in one file · Site-level llms.txt

Eleventy questions

Do I need a shortcode instead of an include?

Either works. An include is simpler to read and keeps the markup in one file; a shortcode is better when the form needs parameters in many places. Both render to the same HTML, so pick the one your templates already use.

How do I give different pages different forms?

Put the form id in front matter and pass it into the include. That keeps the contact page and the waitlist on separate forms, which matters for recipients and rate limits.

Does Eleventy Serverless change anything?

Not for a form. Serverless in Eleventy is about rendering pages on demand; a submission is a post to a different host. Adding a function would put your build in the form path for no gain.

Is there an Eleventy plugin for forms?

There are integrations, but they exist to render markup. Receiving, storing, and delivering the submission is the part the endpoint owns — and it is the part that would otherwise depend on your deploy going well.

Put your forms on a backend that keeps a record.

Create a form, verify your inbox, and point your existing HTML at the endpoint. No server, no SDK, no rewrite.

POST https://freelyform.com/f/FORM_ID