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.
<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.
- 1Create your Form Plume accountStart free and give your Eleventy form a hosted endpoint for email, submissions, spam filtering, uploads, integrations, and webhooks.Start free
- 2Create the form and copy the endpointCopy the endpoint URL from Form Plume. It looks like
https://api.formplume.com/f/your-slug - 3Connect 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.
One line. Zero backend.
