11ty forms to Google Sheets
A data file, an include, and a plain form action. Your Eleventy site stays static and every submission lands as a row in your sheet.
Eleventy's appeal is that it gets out of the way: templates in whatever language you like, a data cascade instead of a framework, and output that is just files. Forms are the one place a static build cannot help you, because someone has to be on the receiving end of the POST. SheetLink Forms is that receiver. You point the form's action at a permanent endpoint of the shape https://sheetlinkforms.com/f/{token}, and each submission lands as a row in Google Sheets or an Excel Online table - no serverless function in your repo, no third-party widget replacing your markup, no client JavaScript required.
Delivery is direct and observable: an asynchronous worker writes straight to the Google Sheets API or Microsoft Graph - no Zapier in the path - retrying at 5 minutes, 30 minutes, and 2 hours, with every attempt recorded in a delivery log in the dashboard. A formula-injection guard escapes leading =, +, -, and @ characters so hostile input stays inert text. There are no per-submission fees, ever.
The integration also happens to fit Eleventy's idioms: the token belongs in the data cascade, the form belongs in an include, and both work identically whether you write Nunjucks, Liquid, or WebC. You can watch a live form feed a public sheet on the demo. SheetLink Forms is free during beta and invite-gated - join the waitlist to get an endpoint.
The best path for 11ty
The best path is a plain form action in an include, with the token supplied by Eleventy's data cascade. Create _data/site.json with a sheetlinkToken key and every template on the site can read site.sheetlinkToken - the snippet below shows Nunjucks, and the Liquid version is character-for-character identical. The endpoint accepts the browser's urlencoded post (bodies capped at 256KB) and answers with a 303 redirect to a hosted thank-you page or a custom redirect URL you configure per form, so the integration works with JavaScript disabled and adds nothing to your build.
The optional sl.js embed is the upgrade path: one script tag in your base layout converts the same form to an AJAX submit with inline success text, auto-injects the honeypot, adds a timing signal used in spam screening, and captures utm_source, utm_medium, utm_campaign, utm_term, and utm_content plus the gclid, wbraid, gbraid, fbclid, and msclkid click IDs from the URL, persisted in localStorage across pages. Add matching columns to your sheet and each lead arrives with its ad attribution attached - rare in this category and useful the first time you ask which campaign a lead came from.
1. Connect a sheet and get your endpoint
Sign in and connect Google Sheets with one-click OAuth. The connection uses the drive.file scope, so SheetLink Forms can only touch sheets you pick in the Google picker or create inside the product - never the rest of your Drive. On Microsoft 365, connect Excel Online instead and pick an Excel table; the table's columns define the row shape. You get a permanent endpoint at https://sheetlinkforms.com/f/{token}.
2. Put the token in the data cascade
Create _data/site.json containing { "sheetlinkToken": "slf_yourtoken" }. Global data files are Eleventy's native answer to "a value every template needs", and the token qualifies: it is safe in public HTML because the endpoint is designed to be public and screened by its own spam layers. Rotating the token later is a one-file edit.
3. Create the include, honeypot included
Save the snippet as _includes/contact-form.njk and place it with {% include "contact-form.njk" %} in any layout or page - the same markup works in Liquid templates unchanged. Name inputs after your column headers and mapping happens automatically; matching is case and punctuation insensitive, so Email, email, and E-mail all land in an Email column. Keep the _slhp honeypot exactly as-is: it is the only signal that marks spam outright, and everything else suspicious is quarantined for one-click review rather than dropped.
4. Optionally add sl.js to your base layout
Drop the script tag before </body> in the layout every page extends. It binds any form whose action points at /f/ (and any form marked data-sheetlink), swaps the full-page redirect for an AJAX submit with inline success text, and layers in the timing signal plus UTM and click-ID capture. It emits sheetlink:success and sheetlink:error events from the form if you want to hang your own UI off a submission - details in the docs.
5. Build, deploy, send a test
Run npx @11ty/eleventy, deploy the output directory anywhere static files are served, and submit a test. On an empty sheet the header row is seeded for you; after that, rows append under the header, aligned to your columns. Watch the attempt land in the delivery log, and see the endpoint-to-sheet pipeline on how it works.
FAQ
Which template languages does this work with?
All of them, because the deliverable is plain HTML. The snippet shows Nunjucks; Liquid renders the identical {{ site.sheetlinkToken }} expression, and in WebC or JavaScript templates you interpolate the same value however that language does it. Eleventy never needs to know the form exists.
Do I need an Eleventy plugin or serverless function?
No. The form posts from the visitor's browser directly to the SheetLink Forms endpoint. There is nothing to install, no function to deploy alongside the site, and no build-time dependency - which also means nothing to break when you upgrade Eleventy.
What happens if the visitor has JavaScript disabled?
The form still works. A bare action + method="POST" form posts urlencoded data and the endpoint answers with a 303 redirect to a hosted thank-you page or your custom redirect URL. sl.js is strictly an enhancement layer.
How do I get UTM parameters and ad click IDs into the sheet?
Add the sl.js embed and create matching columns - utm_source, utm_campaign, gclid, and so on. The script captures them from the landing URL, persists them in localStorage while the visitor browses, and sends them with the eventual submission so the lead arrives with its attribution attached.
What does it cost?
Free during beta, invite required - join the waitlist. Planned post-beta pricing is a free tier around 50 submissions a month and a paid plan around $15-19/mo, unmetered. No per-submission or per-task fees, ever.
Also on: Astro · Jekyll · Netlify-hosted sites