Skip to main content

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 reactive object form, bind its fields through v-model, and work with it like a regular JS object.


A basic form example with reactive

vue
<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:

  • form is a single reactive object.
  • In the template we just write v-model="form.field".
  • No .value - with reactive you work with it like a regular object.

Resetting the form with reactive

The usual way to do it:

js
const initialForm = { name: '', email: '', age: null, agree: false, } const form = reactive({ ...initialForm }) function reset() { Object.assign(form, initialForm) }

In the template:

html
<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:

js
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:

html
<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:

js
const form = reactive({ name: '', email: '' }) const { name, email } = form // this loses reactivity

Then in the template:

html
<input v-model="name" />

reactivity no longer works correctly.

The correct way is either:

html
<input v-model="form.name" />

or use toRefs(form) if you really want to destructure:

js
import { reactive, toRefs } from 'vue' const form = reactive({ name: '', email: '' }) const { name, email } = toRefs(form) // good

Watching the form (watch)

For example, autosaving a draft:

js
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 ready
Premium

A concise answer to help you respond confidently on this topic during an interview.