How to use reactive for a form?
reactive is convenient to use for a form when you have many fields and want to store them in a single object instead of a dozen separate refs.
The simplest approach:
Create one
reactiveobjectform, bind its fields throughv-model, and work with it like a regular JS object.
A basic form example with reactive
<script setup>
import { reactive } from 'vue'
const form = reactive({
name: '',
email: '',
age: null,
agree: false,
})
function submit() {
console.log('Submitting form:', { ...form })
}
</script>
<template>
<form @submit.prevent="submit">
<input v-model="form.name" placeholder="Name" />
<input v-model="form.email" placeholder="Email" />
<input v-model.number="form.age" type="number" placeholder="Age" />
<label>
<input type="checkbox" v-model="form.agree" />
I agree to the terms
</label>
<button type="submit">Submit</button>
</form>
</template>What matters here:
formis a single reactive object.- In the template we just write
v-model="form.field". - No
.value- withreactiveyou work with it like a regular object.
Resetting the form with reactive
The usual way to do it:
const initialForm = {
name: '',
email: '',
age: null,
agree: false,
}
const form = reactive({ ...initialForm })
function reset() {
Object.assign(form, initialForm)
}In the template:
<button type="button" @click="reset">Reset</button>This way form stays the same reactive object, but its fields go back to the initial state.
Validation with reactive
You can make an errors object:
const form = reactive({
email: '',
password: '',
})
const errors = reactive({
email: '',
password: '',
})
function validate() {
errors.email = ''
errors.password = ''
if (!form.email.includes('@')) {
errors.email = 'Invalid email'
}
if (form.password.length < 6) {
errors.password = 'Minimum 6 characters'
}
return !errors.email && !errors.password
}
function submit() {
if (!validate()) return
// submit
}Template:
<input v-model="form.email" />
<p v-if="errors.email" class="error">{{ errors.email }}</p>reactive vs ref for a form
When reactive is better:
- many form fields, convenient to keep in one object;
- you need to pass the whole form somewhere at once (into a function / API);
- there are nested structures (
address.city,profile.contacts.phone).
When ref is fine too:
- one or two fields;
- simple components where an object is excessive.
A nuance: don't destructure reactive in the template
If you do this:
const form = reactive({ name: '', email: '' })
const { name, email } = form // this loses reactivityThen in the template:
<input v-model="name" />reactivity no longer works correctly.
The correct way is either:
<input v-model="form.name" />or use toRefs(form) if you really want to destructure:
import { reactive, toRefs } from 'vue'
const form = reactive({ name: '', email: '' })
const { name, email } = toRefs(form) // goodWatching the form (watch)
For example, autosaving a draft:
import { reactive, watch } from 'vue'
const form = reactive({
title: '',
content: '',
})
watch(
() => ({ ...form }), // create a new object so watch sees the changes
(val) => {
console.log('Saving draft', val)
},
{ deep: true }
)Short Answer
Interview readyA concise answer to help you respond confidently on this topic during an interview.