Add a fully functional contact form to your Vue.js app using v-model bindings and fetch. No backend server required. FormsList handles everything.
TL;DR
To add a contact form to Vue.js, create a component with v-model bindings and submit via fetch to `https://formslist.com/f/your-form-id`. FormsList handles storage, notifications, and spam protection. No backend server required.
Sign up for a free FormsList account and create a new form. Copy the unique endpoint URL from your dashboard. You will use it as the fetch URL in your Vue component.
// Your endpoint will look like this:
// https://formslist.com/f/YOUR_FORM_HASHCreate a new single-file component using the Composition API with <script setup>. We use reactive refs for each field and a status ref to manage submission state. The handleSubmit function sends form data to FormsList via fetch.
<script setup lang="ts">
import { ref } from 'vue'
const name = ref('')
const email = ref('')
const message = ref('')
const status = ref<'idle' | 'sending' | 'sent' | 'error'>('idle')
async function handleSubmit() {
status.value = 'sending'
const formData = new FormData()
formData.append('name', name.value)
formData.append('email', email.value)
formData.append('message', message.value)
try {
const res = await fetch('https://formslist.com/f/YOUR_FORM_HASH', {
method: 'POST',
body: formData,
})
if (res.ok) {
status.value = 'sent'
name.value = ''
email.value = ''
message.value = ''
} else {
status.value = 'error'
}
} catch {
status.value = 'error'
}
}
</script>
<template>
<form @submit.prevent="handleSubmit" class="space-y-4 max-w-md">
<div>
<label for="name" class="block text-sm font-medium">Name</label>
<input
id="name"
v-model="name"
type="text"
required
class="mt-1 block w-full rounded border p-2"
/>
</div>
<div>
<label for="email" class="block text-sm font-medium">Email</label>
<input
id="email"
v-model="email"
type="email"
required
class="mt-1 block w-full rounded border p-2"
/>
</div>
<div>
<label for="message" class="block text-sm font-medium">Message</label>
<textarea
id="message"
v-model="message"
rows="4"
required
class="mt-1 block w-full rounded border p-2"
/>
</div>
<button
type="submit"
:disabled="status === 'sending'"
class="rounded bg-blue-600 px-4 py-2 text-white hover:bg-blue-700 disabled:opacity-50"
>
{{ status === 'sending' ? 'Sending...' : 'Send Message' }}
</button>
<p v-if="status === 'sent'" class="text-green-600">Message sent successfully!</p>
<p v-if="status === 'error'" class="text-red-600">Something went wrong. Please try again.</p>
</form>
</template>Import and use your ContactForm component in any Vue page or parent component. If you are using Vue Router, create a /contact route that renders this component.
<script setup>
import ContactForm from '@/components/ContactForm.vue'
</script>
<template>
<main class="mx-auto max-w-2xl py-12 px-4">
<h1 class="text-3xl font-bold mb-6">Contact Us</h1>
<ContactForm />
</main>
</template>Submit a test message and check your FormsList dashboard to confirm it arrived. Enable email notifications, set up auto-responders, or connect integrations like Slack and Google Sheets from the dashboard.
Yes. Replace the <script setup> syntax with the Options API: use data() for reactive fields and methods for handleSubmit. The fetch call to FormsList works identically in Vue 2 and Vue 3.
You can use native HTML5 validation attributes (required, type="email") or a library like VeeValidate or Vuelidate. FormsList also performs server-side spam checks regardless of client validation.
Absolutely. The same Vue component works in Nuxt 3. Place it in your components/ directory and use it in any Nuxt page. No server routes or API middleware are needed.
200 free submissions a month and a live endpoint in 30 seconds. No server, no credit card, just point your form at it.