Eleventy

Add a contact form to an Eleventy site with a Nunjucks include and the access key in global data.

Eleventy writes static HTML to _site, so there is nothing on your host that can accept a form post. Point the form at SilentForms instead and each submission is emailed to you. In Eleventy terms that means a Nunjucks include for the form, a global data file for the access key and site URL, and a thank-you page with its own permalink. The examples below use Eleventy v3 and the default folders (_includes and _data inside the input directory).

1. Get an access key#

Enter your email on the access key page and click the confirmation link. The key only routes submissions to you, so it is safe in the generated HTML.

2. Add a global data file#

Every file in _data becomes a global variable named after the file. The simplest version is _data/site.json:

{
  "title": "My site",
  "url": "https://example.com",
  "silentformsKey": "YOUR_ACCESS_KEY_HERE"
}

Templates can now read site.silentformsKey and site.url.

If you would rather keep the values out of the repository, or want the thank-you redirect to point at localhost while developing, use _data/site.js instead. JavaScript data files can read process.env:

export default {
  title: 'My site',
  url: process.env.SITE_URL || 'http://localhost:8080',
  silentformsKey: process.env.SILENTFORMS_KEY || 'YOUR_ACCESS_KEY_HERE',
};

export default needs ESM, which Eleventy supports from v3: either set "type": "module" in package.json or name the file site.mjs. In a CommonJS project, write module.exports = { ... } instead. Keep only one of site.json and site.js.

3. Create the include#

Create _includes/contact-form.njk:

<form action="https://silentforms.com/api/submit" method="POST" class="contact-form">
  <input type="hidden" name="accessKey" value="{{ site.silentformsKey }}">
  <input type="hidden" name="subject" value="New message from {{ site.title }}">
  <input type="hidden" name="redirect" value="{{ site.url }}/thanks/">

  <label for="cf-name">Name</label>
  <input id="cf-name" type="text" name="name" autocomplete="name" required>

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

  <label for="cf-message">Message</label>
  <textarea id="cf-message" name="message" rows="6" required></textarea>

  <!-- Honeypot: hidden from people, filled in by bots -->
  <input type="checkbox" name="botcheck" style="display: none;" tabindex="-1" autocomplete="off">

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

The redirect value is built from site.url because it must be an absolute URL. A relative path like /thanks/ is rejected with "redirect must be an absolute http(s) URL." Nunjucks escapes output by default, so a site title with quotes cannot break the attribute.

4. Add the contact page#

Create contact.njk in your input directory:

---
layout: base.njk
title: Contact
permalink: /contact/
---
<h1>Contact us</h1>
<p>Questions or feedback? We reply to every message.</p>

{% include "contact-form.njk" %}

Swap base.njk for whatever layout your site uses. Using a .njk page rather than a .md one matters: Eleventy pre-processes Markdown files with Liquid by default, not Nunjucks, so a Nunjucks include in Markdown depends on your markdownTemplateEngine setting.

5. Add the thank-you page#

Create thanks.njk:

---
layout: base.njk
title: Thanks!
permalink: /thanks/
eleventyExcludeFromCollections: true
---
<h1>Thanks!</h1>
<p>Your message is on its way. We will get back to you soon.</p>
<p><a href="/">Back to the homepage</a></p>

permalink puts the page at /thanks/index.html, which matches the redirect in the include. eleventyExcludeFromCollections keeps it out of collections.all, so it does not show up in a sitemap or navigation built from collections. Without the redirect field, visitors see the default SilentForms success page instead. See Redirect after submit.

Test it#

Run the development server:

npx @11ty/eleventy --serve

Open http://localhost:8080/contact/, send yourself a message and check that you land on /thanks/ and the email arrives. If you used site.json, the redirect goes to the production URL even locally; that is expected.

For the production build, set the variables your data file reads:

SITE_URL=https://example.com npx @11ty/eleventy

Then check _site/contact/index.html: the hidden fields should hold your key and https://example.com/thanks/.

Gotchas#

  • The access key field is empty. The data file name and the variable do not match (_data/site.json is site), or both site.json and site.js exist.
  • SyntaxError: Unexpected token 'export'. The data file uses ESM in a CommonJS project. Rename it to site.mjs or use module.exports.
  • The redirect URL has a double slash. Leave the trailing slash off site.url.
  • The include is not found. Includes are resolved from _includes inside the input directory. If you changed dir.input or dir.includes in your Eleventy config file, move the file accordingly.

For every error message, see Troubleshooting.

Next steps#