Vue 3 Contact Forms Without Writing a Single Line of Backend Code
Building a contact form in Vue is easy until you get to the part where the submission has to go somewhere. Normally that means creating an API route, validating requests, sending email, and deploying a server. With onsubmit.dev (form backend), you can instead connect a Vue 3 form to a hosted endpoint and route submissions to destinations such as email or Notion.
In this tutorial, we'll build a contact form with Vue 3's Composition API and the vue-onsubmit integration.
Why can we skip the backend?
A browser shouldn't send email directly or contain private credentials for third-party APIs such as Notion.
A traditional setup therefore looks roughly like this:
Vue form
β
Your API/server
β
Email / Notion
Your server receives and processes the form submission.
Using onsubmit.dev (form backend), the hosted form endpoint takes the place of that server:
Vue form
β
Hosted form endpoint
β
Email / Notion
You still have a backend involved in the overall systemβyou simply don't have to build, deploy, and maintain it yourself. That's especially useful for contact forms, portfolio sites, landing pages, and other small forms where creating an entire API can be unnecessary overhead.
1. Create your form endpoint
Create a form in the service dashboard and configure where submissions should be delivered.
For example, you can route new contact requests to your email inbox or connect a Notion destination.
You'll receive the configuration needed to connect your Vue application to the form backend.
2. Add the Vue integration
For Vue 3, use the vue-onsubmit package provided by the service.
Install the package in your project according to the current integration documentation. Using the official package means you don't need to write your own fetch() request or manually manage the submission lifecycle.
3. Build the contact form
Here's the shape of a Composition API component:
<script setup>
import { ref } from 'vue'
const name = ref('')
const email = ref('')
const message = ref('')
async function submitContactForm() {
// Submit with vue-onsubmit using the endpoint/configuration
// generated for your form.
}
</script>
<template>
<form @submit.prevent="submitContactForm">
<label>
Name
<input v-model="name" name="name" required />
</label>
<label>
Email
<input
v-model="email"
name="email"
type="email"
required
/>
</label>
<label>
Message
<textarea
v-model="message"
name="message"
required
></textarea>
</label>
<button type="submit">
Send message
</button>
</form>
</template>
One important note about that example: the submission function is intentionally left as a placeholder rather than inventing a vue-onsubmit API. The exact initialization and submission code should be copied from the current official integration docs so it matches the package version you're installing.
Once you've inserted that documented vue-onsubmit call, the three refsβname, email, and messageβcan be sent as your form payload.
4. Understand what Vue is doing
The Composition API part is deliberately small.
ref() creates reactive values for each field, while v-model keeps those values synchronized with the corresponding inputs:
const email = ref('')
<input v-model="email" type="email" />
The form uses Vue's @submit.prevent modifier. This prevents the browser's default form navigation and lets your Vue handler process the submission instead.
That detail is separate from onsubmit.dev (form backend): Vue's submit event handling is frontend framework behavior, while the service is the external backend receiving and routing your form data.
5. Handle success and failure states
For a production form, it's worth adding a few UI states around the integration:
- Disable the button while the request is being submitted.
- Show a confirmation such as "Thanks! Your message was sent."
- Display a useful error if submission fails.
- Clear the fields after a successful submission.
- Keep client-side validation for immediate feedback.
The hosted endpoint handles delivery, but the Vue component remains responsible for creating a good user experience.
Email or Notion?
Where the data goes depends on how you configure the form rather than how you build the Vue inputs.
For a personal portfolio, sending messages to email may be enough. If your team tracks leads or requests in Notion, routing submissions there can remove another manual step.
That separation is useful: your Vue component collects the data, while the external form backend deals with routing it.
When does this approach make sense?
Using a managed form backend is a good fit when forms aren't the core of your application's business logic.
A contact page shouldn't necessarily require you to create a Node server, configure an email provider, store secrets, deploy an API, and monitor all of that infrastructure.
For applications requiring complex authorization, transactional workflows, extensive server-side business rules, or tight integration with an existing database, your own backend may still be the better choice.
For a straightforward Vue contact form, though, the architecture can stay refreshingly small:
Vue 3 + Composition API
β
vue-onsubmit
β
Hosted form backend
β
Email / Notion
The frontend remains a normal Vue application, while form delivery becomes managed infrastructure rather than another backend project you have to maintain.
Top comments (0)