Netlify Forms šŸ’š Nuxt Static Sites

Posted 2 months ago
Return to overview

First things first, it's good to read up on the docs, which explain how forms would work from a generic viewpoint. There not a lot you need to do in the Netlify console yet, apart from flipping on the switch to actively look for forms during the build and deployment process. It's found on the Forms section.

A Static Form for a Static Site

As de documentation of Netlify dictates, we need to have a static form (so that Netlify can ingest it's properties), even when you're doing Server Side Rendering (SSR) as a site strategy. In order to have the form present, is by creating an HTML document and placing it in Nuxts `public` folder. Naming is important, because of referencing and detection!

Location is equally important, because Netlify needs to be able to detect the form! In our little example, we'll create a file called: `the-contact-form.html` in our `public` folder, with the following contents:

<html> <body> <form name="the-contact-form" data-netlify="true" data-netlify-honeypot="bot-field" method="POST" action="/?sent=true" > <input type="hidden" name="form-name" value="the-contact-form" /> <label for="bot-field"> Don't forget to fill this out: <input name="bot-field" required /> </label> <fieldset> <label for="email">Email:</label> <input type="email" name="email" required placeholder="Your email"> </fieldset> <button type="submit">Send</button> </form> </body> </html>

A couple of things to notice here. We're not actually going to use this form in our application. This is just for the build process. Be aware that the form data of this form needs to match your applications' form field by field.

On the `<form>` tag, we set a couple of attributes for Netlify to pick up. The `data-netlify="true"` signals Netlify that there's a form to process. The `name="the-contact-form"` is the reference. We've also set a `data-netlify-honeypot="bot-field"` to prevent automated submissions as much as possible. (As an experiment, I've made the field required in the actual form, but I've no data on whether this helps or not.) Forms need to be submitted via POST and I've provided an existing target for the POST as well (you can just send it to your index if needed, like I did).

For SSR, we need to additionally provide the name of the form as a value, so that's where the `hidden` field is for.

After your site has been generated, you should be able to directly access this HTML form by visiting it's URL.

An Interactive Form in your Application

Now you're ready to add your form to your application. Again, there are some rules to follow, which are basically stated in the docs as well. I've created this example with some Vue & Nuxt flavours.

<script setup lang="ts"> import { ref } from 'vue'; const sent = ref<boolean>(false); const email = ref<string>(''); const submitted = ref<boolean>(sent.value || false); const handleSubmit = async (event: Event): Promise<boolean> => { event.preventDefault(); const myForm = event.target as HTMLFormElement; const formData = new FormData(myForm) as FormData; try { const result = (await fetch(`/contact-initial-form.html`, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: new URLSearchParams(formData).toString(), })) if (result) return true return false } catch (error) { console.error('error caught:', error) return false } finally { // Feel free to add any additional logic here, // such as resetting the form, rerouting or showing a success message submitted.value = true; } }; </script> <template> <ClientOnly> <form name="the-contact-form" method="POST" @submit.prevent="handleSubmit"> <input type="hidden" name="form-name" value="the-contact-form" /> <p class="begone"> <label for="bot-field"> Don't forget this field!: </label> <!-- eslint-disable-next-line vue/html-self-closing --> <input id="bot-field" name="bot-field" /> </p> <label for="email">Email</label> <input id="email" v-model="email" type="email" name="email" required :disabled="sent" /> <button type="submit" :disabled="sent">Submit</button> </form> <div class="feedback" :class="{ 'visually-hidden': !submitted }"> Success! </div> </ClientOnly> </template> <style lang="css" scoped> .begone, /* uses a different css class to try and outsmart bots */ .visually-hidden { position: absolute; left: -9999px; height: 0rem; width: 0rem; overflow: hidden; opacity: 0; pointer-events: none; } </style>

There's one thing which isn't explicitly mentioned in the docs, but critical: Make sure your `fetch` targets the location of the static form! If you follow the example and link to the root, your form will not be able to receive submissions! In our case, fetch targets `/the-contact-form.html`.

Based on the example, you should be able to modify it for you own liking. Enjoy!

Enjoyed this tutorial? I’m available for part-time remote work helping teams with architecture, web development (Vue/Nuxt preferred), performance and advocacy.

Return to overview