Frameworks and libraries
Add a contact form to a Vue app
Vue's reactivity makes the form itself easy; it is everything after submit that costs a weekend. WebForms handles the after: a POST from your component gets validated, spam-filtered, stored and routed wherever you like. The examples use the Composition API with `<script setup>`, and work identically in Vue 3 SPAs, Nuxt and Vue sprinkled into a static page.
Setup
Create your endpoint
Make a form in the dashboard and copy the endpoint URL it gives you.
Post from your component
Either pass the native FormData through, or bind fields with v-model and send JSON. Both examples are below.
Route the submissions
Add email, Slack, Discord, Telegram or webhook destinations in the dashboard. No redeploy needed when you change them.
Vue examples
Swap in your own endpoint and these run as-is.
FormData version - no per-field refs needed.
<script setup>
import { ref } from "vue";
const status = ref("idle");
const error = ref(null);
async function onSubmit(e) {
status.value = "sending";
const res = await fetch("https://send.webforms.to/wft_your_key", {
method: "POST",
headers: { accept: "application/json" },
body: new FormData(e.target),
});
const data = await res.json();
if (data.ok) {
status.value = "sent";
} else {
error.value = data.message;
status.value = "error";
}
}
</script>
<template>
<p v-if="status === 'sent'" role="status">Thanks, we got it.</p>
<form v-else @submit.prevent="onSubmit">
<input name="name" required />
<input name="email" type="email" required />
<textarea name="message" required />
<!-- honeypot -->
<input name="_gotcha" tabindex="-1" aria-hidden="true"
style="position:absolute;left:-9999px" />
<button :disabled="status === 'sending'">
{{ status === 'sending' ? 'Sending…' : 'Send' }}
</button>
<p v-if="error" role="alert">{{ error }}</p>
</form>
</template>v-model version, posting JSON.
<script setup>
import { reactive, ref } from "vue";
const form = reactive({ name: "", email: "", message: "" });
const sending = ref(false);
const sent = ref(false);
async function onSubmit() {
sending.value = true;
const res = await fetch("https://send.webforms.to/wft_your_key", {
method: "POST",
headers: {
"content-type": "application/json",
accept: "application/json",
},
body: JSON.stringify(form),
});
sending.value = false;
sent.value = res.ok;
}
</script>
<template>
<p v-if="sent">Thanks, we got it.</p>
<form v-else @submit.prevent="onSubmit">
<input v-model="form.name" required />
<input v-model="form.email" type="email" required />
<textarea v-model="form.message" required />
<button :disabled="sending">Send</button>
</form>
</template>Vue form FAQ
No. Vue posts straight to your endpoint from the browser. Nothing else to host.
Yes, and there is a dedicated Nuxt guide covering server routes and static generation.
If a form has field rules configured, a failed submission returns 422 with an issues array naming each field and message. Map that onto your own error refs.
Related guides
Ready to wire up your Vue form?
Free for 300 submissions a month. Endpoint in under a minute, no card required.
Start free