How to Build a Nuxt Contact Form

Last updated: March 2026

Nuxt is a full-stack Vue framework, but you don't need to write server routes just to handle a contact form. With a form backend service, you can add a fully functional contact form to any Nuxt project, static or server-rendered, using a simple Vue component.

This guide walks through adding a working contact form to a Nuxt app with Vue 3 Composition API, spam protection, and email notifications. No Nitro server routes required.

How it works

Your Nuxt component renders a form. On submit, the data goes to a FormWit endpoint via a POST request. FormWit processes it, filters spam, and sends you an email. No Nitro server routes, no email configuration, no database setup.

The flow looks like this:

  1. A visitor fills out your contact form in the browser
  2. Your Vue component sends the form data to your FormWit endpoint
  3. FormWit validates the data, checks for spam, and stores the submission
  4. You receive an email notification and can view the submission in your dashboard

Add a contact form to Nuxt

Step 1: Create a FormWit account

Go to app.formwit.com/auth/signup and create a free account. No credit card required. Once signed in, click Create Form and give it a name (e.g., "Contact Form"). You'll get a unique endpoint URL that looks like https://app.formwit.com/api/s/YOUR_FORM_ID.

Step 2: Create the contact form component

Create a new file called ContactForm.vue in your Nuxt project's components/ directory. This component handles form submission via fetch, tracks the submission status with ref, and includes a honeypot field for spam protection.

<script setup>
import { ref } from 'vue'

const status = ref('idle')
const FORM_URL = 'https://app.formwit.com/api/s/YOUR_FORM_ID'

async function handleSubmit(e) {
  status.value = 'sending'

  try {
    const response = await fetch(FORM_URL, {
      method: 'POST',
      body: new FormData(e.target),
    })

    if (response.ok) {
      status.value = 'success'
      e.target.reset()
    } else {
      status.value = 'error'
    }
  } catch {
    status.value = 'error'
  }
}
</script>

<template>
  <form :action="FORM_URL" method="POST" @submit.prevent="handleSubmit">
    <label for="name">Name</label>
    <input type="text" id="name" name="name" required />

    <label for="email">Email</label>
    <input type="email" id="email" name="email" required />

    <label for="message">Message</label>
    <textarea id="message" name="message" required></textarea>

    <!-- Honeypot spam protection -->
    <input type="text" name="_gotcha" style="display:none" tabindex="-1" autocomplete="off" />

    <button type="submit" :disabled="status === 'sending'">
      {{ status === 'sending' ? 'Sending...' : 'Send Message' }}
    </button>

    <p v-if="status === 'success'" style="color:#16a34a">Message sent!</p>
    <p v-if="status === 'error'" style="color:#dc2626">Something went wrong. Try again.</p>
  </form>
</template>

Replace YOUR_FORM_ID with the endpoint ID from your FormWit dashboard. The component uses FormData to serialize the form fields and sends them via fetch. The hidden _gotcha field is a honeypot. Bots fill it in, humans don't, so FormWit uses it to filter spam.

Step 3: Use the component

Nuxt auto-imports components from the components/ directory, so you can use ContactForm directly in any page without an import statement:

<template>
  <div>
    <h1>Contact Us</h1>
    <ContactForm />
  </div>
</template>

Step 4: Test it

Run your Nuxt development server with npx nuxi dev, fill out the form, and hit submit. You should see the success message appear in the browser. Check your FormWit dashboard (the submission will be listed there) and check your email for the notification.

Works with all Nuxt modes

This approach works with every Nuxt rendering strategy:

  • nuxt generate (static) - the form is rendered at build time and submissions happen client-side.
  • nuxt build (SSR) - the form is server-rendered on first load, then hydrates and submits client-side.
  • Hybrid rendering - works regardless of which routes are prerendered and which are server-rendered.

Because the form submission goes directly from the browser to FormWit, the rendering mode doesn't matter. No Nitro server routes or API handlers are needed.

Custom redirect after submission

If you want to redirect users to a thank-you page after submission instead of showing an inline message, add a hidden redirect_to field to your form:

<input type="hidden" name="redirect_to" value="https://yoursite.com/thank-you" />

This tells FormWit to redirect the browser to that URL after a successful submission. The redirect_to value must be a valid http or https URL. If you're handling submission via fetch (as in the component above), you'll need to handle the redirect in your JavaScript instead:

if (response.ok) {
  window.location.href = 'https://yoursite.com/thank-you';
}

Summary

Adding a contact form to a Nuxt app doesn't require Nitro server routes, email configuration, or database setup. With FormWit, you create a form endpoint, build a standard Vue component, and let the service handle spam filtering, data storage, and email notifications.

FormWit's free plan includes unlimited forms, 100 submissions per month, email notifications, and built-in spam protection. Create your free account and add a contact form to your Nuxt app in minutes.

Related guides: Vue contact form · Vercel contact form · HTML contact form · Spam protection · Contact form templates

Frequently asked questions

Does FormWit work with Nuxt 3?

Yes. FormWit works with Nuxt 3 in all rendering modes: static (nuxt generate), SSR (nuxt build), and hybrid. The form submission happens client-side via fetch, so no Nitro server routes are needed.

Can I use useFetch with FormWit?

You can, but it is not recommended for form submissions. Nuxt's useFetch is designed for data fetching with SSR support, not for sending POST requests from event handlers. Use the standard fetch API inside your submit handler instead. It is simpler and avoids unnecessary SSR complexity.

Does it work with Nuxt generate?

Yes. nuxt generate produces static HTML files. The form is rendered at build time and submissions happen client-side at runtime. FormWit's endpoint receives the POST request directly from the visitor's browser, so static generation works perfectly.

Want to skip the setup?

FormWit gives you a form endpoint in 60 seconds. Free plan, no credit card.

Create Free Form

Need a form fast?

Build one visually with our free HTML form generator — no coding required.

Try the Form Generator →

Add FormWit to your Nuxt app

Add a contact form to your site in 30 seconds. No backend code required.

Try FormWit Free