Skip to main content
New:Build with AI agents

Eleventy contact forms
without serverless functions.

Keep your Eleventy site static and point the generated form at Form Plume for email notifications, dashboard storage, spam filtering, file uploads, integrations, and signed webhooks.

Free foreverNo credit cardEleventy endpoint ready in under a minute
_includes/partials/contact-form.njk
<form action="{{ formplume.endpoint }}" method="POST">
  <input type="hidden" name="source" value="eleventy">
  <input name="name" autocomplete="name" required>
  <input name="email" type="email" autocomplete="email" required>
  <textarea name="message" required></textarea>
  <button type="submit">Send</button>
</form>

The full guide

A hosted backend for Eleventy includes.

Use Eleventy data and includes to generate a plain form that works from any static host.

  1. 1
    Create your Form Plume accountStart free and give your Eleventy form a hosted endpoint for email, submissions, spam filtering, uploads, integrations, and webhooks.Start free
  2. 2
    Create the form and copy the endpointCopy the endpoint URL from Form Plume. It looks like https://api.formplume.com/f/your-slug
  3. 3
    Connect it inside EleventyPut the endpoint in Eleventy data, render it in a Nunjucks include or layout, build the site, and verify the generated HTML. Send one test submission from the real Eleventy app or preview environment and confirm it reaches Form Plume.

You can use this AI prompt to hand the platform-specific wiring to an assistant without accidentally creating a backend.

Eleventy can stay fully static

Eleventy produces static HTML, but a form can still submit to a hosted backend. Put the form in a Nunjucks include, layout, or component and let Form Plume handle the operational side after submit.

<form action="{{ formplume.endpoint }}" method="POST">
  <input type="hidden" name="source" value="eleventy">
  <input type="hidden" name="_redirect" value="/thanks/">
 
  <label for="contact-name">Name</label>
  <input id="contact-name" name="name" autocomplete="name" required>
 
  <label for="contact-email">Email</label>
  <input id="contact-email" type="email" name="email" autocomplete="email" required>
 
  <label for="contact-message">Message</label>
  <textarea id="contact-message" name="message" rows="5" required></textarea>
 
  <button type="submit">Send message</button>
</form>

Render it from a page with your include convention.

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

Put the endpoint in data

Use an Eleventy data file so the endpoint is not hard-coded in several templates.

export default {
  endpoint: "PASTE_YOUR_FORM_PLUME_ENDPOINT_HERE",
};

After npx @11ty/eleventy, inspect the generated _site page and confirm the rendered form has the endpoint, method="POST", and named fields.

Uploads

Eleventy does not need an upload plugin. The generated HTML needs multipart encoding.

<form
  action="{{ formplume.endpoint }}"
  method="POST"
  enctype="multipart/form-data"
>
  <input type="file" name="attachment" accept=".pdf,.png,.jpg">
  <button type="submit">Send file</button>
</form>

Do not convert uploads to JSON. Let the browser send multipart form data directly to Form Plume.

What Form Plume handles

Once the static page posts the submission, Form Plume sends email notifications, stores the message, filters spam, accepts file uploads, and triggers integrations or signed webhooks.

Test from the deployed Eleventy site once, especially when using _redirect. Local preview URLs can hide path and domain issues that only appear after publish.

Primary sources

FAQ

Eleventy form questions
before deploy.

Yes. Eleventy outputs static HTML, and the browser can post the generated form directly to Form Plume.

One line. Zero backend.

The form backend you don’t have to build.

Free foreverNo credit cardSet up in under a minute