Using Alpine.js in a Netlify form

Published October 28th, 2021

I’ve been working on a site in Statamic where the client wants to have their site deployed on Netlify. Working locally, Alpine.js has been great - validation, loading states, and a submit action for the submission process. The build process for a Statamic site with the Statamic SSG is easy. But forms were a bit, well, frustrating.

The problem

Basically, when the site was deployed to Netlify, the form was not detected. This means that trying to submit the form resulted in a 404.

But why? I had the data-netlify attribute, I even tried just the “netlify” approach, but no dice.

Debugging the issue

Looking at the deployment log, there was no mention of forms at all - even though form detection is enabled as part of the deployment.

So how do you debug something? Go back to basics, right?

Basics in this case means: forget about Ajax submission, forget about Statamic generating my form, forget about honeypots - bare bones form, nothing fancy.

Deploying through Netlify shows that the form is detected, and picks up the fields that I had in the form. OK, good, so Netlify’s detection is working - but not with my Statamic output.

One-by-one, I added attributes back in, and as soon as I added the Alpine.js x-on:submit attribute, Netlify stopped detecting the form. I tried different placement’s of Netlify’s requested attributes, but nothing would get detected. I even tried to manually add the hidden "form-name" field to the form, but Netlify still didn't detect my form.

Yay, found the culprit, but boo, I want my Alpine.js form magic back too.

A fix for Alpine.js forms and Netlify

With such generic terms to search for including tech keywords and “404”, it was a troublesome Google experience. But I eventually stumbled up on this gist that was specifically for Alpine.js forms in Netlify with AJAX submission. Exactly what was needed (but just made up of too many generic keywords).

Essentially the solution is simple:

  1. Have a hidden form, with all of Netlify’s form detection tags, no styling and bare bones form fields, and then

  2. Have your actual form, with your layout, Alpine.js behaviour and anything else fancy that you (or your client) desire

The logic here is that Netlify detect’s the hidden form (which it does) and each form field in the hidden form (which it does) so that when our real non-hidden form is submitted via AJAX, Netlify knows to expect that form name with those form fields. Netlify will do its magic on the hidden form - including injecting the required hidden "form-name" field - and leave our fancy Alpine.js form alone.

In this example, your Alpine.js x-data and submit behaviours could be inline in your markup, or as part of a global component in your project - that call is up to you, so that level of detail has been omitted.

<form hidden name="my-form-name" data-netlify-honeypot="bot-field" data-netlify="true">
    <input name="name">
    <input name="email">
    <input name="options[]">
    <input name="message">
    <input name="bot-field">
</form>
<form method="POST" x-data="{ ... }" x-on:submit.prevent="...">
    <!-- 
        Form layout that the user actually fills out 
        Make sure each form field has a matching hidden field in the form above
    -->
</form>

Essentially it’s tricking Netlify’s form detection.

The catch here is that the hidden form needs to include the exact same fields/names as your actual form. You could replicate your entire form (one with and one without the Alpine.js code), but if you’re using an id/for combination for fields/labels, creates duplicate IDs in your page - so that’s a no-go. It makes logical sense to have a hidden barebones form with the minimal information required, and a fancy pants user-facing form.

In Statamic, it is essentially running the {{ form }} tag twice - in one I have the actual form layout, labels and styles, but in the other I am manually outputting an input with the correct name (i.e. the handle of the field, or as an array for checkboxes). Netlify doesn’t care if it is an input in your fake form but a select in your real form - it just wants field names to learn what it should expect to receive.

The key things to remember are:

  1. Each field must have a matching field in each form

  2. Make sure your forms still produce valid markup (such as no duplicate IDs, form names, etc)

Other than that, your barebones form can be as basic as you can be - input, with a name, and that’s it. It’s your form that Netlify can’t detect that has the pretty parts, the smart parts, and the Alpine.js goodness: it doesn’t matter that Netlify can’t detect it, because its simply matching names from the fake form.


While this does irk the perfectionist side of me by cluttering the markup, it does neatly solve the issue of an Alpine.js form submitting through AJAX to Netlify (and having Netlify correct auto-detect the form and its fields). After a few hours of playing and testing, finding the gist was a life-saver, especially with constantly running deployments on Netlify to test one little thing. Hopefully you find this useful for this very specific problem.

You may be interested in...