How to use reactive() in Vue 3
reactive() creates a deeply reactive proxy of a plain object, making Vue track all nested property changes automatically — unlike ref() which wraps a single value.
As the creator of CoreUI with Vue development experience since 2014, I use reactive() when managing objects with multiple related properties that should be kept together, such as form state or a user profile.
The key difference from ref() is that reactive() returns the object directly — no .value wrapper — making it feel more natural for complex objects.
Understanding when to use reactive() vs ref() is one of the most important decisions in Vue 3 Composition API code.
Create a reactive object with reactive() and update its properties directly.
import { reactive } from 'vue'
const user = reactive({
name: '',
email: '',
role: 'user',
preferences: {
theme: 'light',
language: 'en'
}
})
// Update nested properties directly - Vue tracks all changes
user.name = 'Alice'
user.preferences.theme = 'dark'
console.log(user.name) // 'Alice' - no .value needed
Unlike ref(), properties of a reactive() object are accessed directly without .value. Vue uses JavaScript Proxies to intercept property reads and writes, automatically triggering re-renders when values change.
Using reactive() for Form State
Group related form fields into a single reactive object.
<template>
<form @submit.prevent="submit">
<input v-model="form.name" placeholder="Name" />
<input v-model="form.email" type="email" placeholder="Email" />
<select v-model="form.role">
<option value="user">User</option>
<option value="admin">Admin</option>
</select>
<p v-if="errors.name">{{ errors.name }}</p>
<button type="submit" :disabled="submitting">
{{ submitting ? 'Saving...' : 'Save' }}
</button>
</form>
</template>
<script setup>
import { reactive } from 'vue'
const form = reactive({
name: '',
email: '',
role: 'user'
})
const errors = reactive({
name: '',
email: ''
})
let submitting = false
function validate() {
errors.name = form.name.trim() ? '' : 'Name is required'
errors.email = form.email.includes('@') ? '' : 'Valid email required'
return !errors.name && !errors.email
}
async function submit() {
if (!validate()) return
submitting = true
await fetch('/api/users', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(form)
})
submitting = false
}
</script>
v-model works directly with reactive() object properties. All form fields share a single reactive container, making it easy to reset the form by reassigning all properties or submit the whole object as JSON.
Limitations of reactive()
Destructuring breaks reactivity — use toRefs() to preserve it.
import { reactive, toRefs } from 'vue'
const state = reactive({ count: 0, message: 'hello' })
// ❌ Destructuring loses reactivity
const { count, message } = state
count++ // Does NOT trigger Vue updates
// ✅ toRefs preserves reactivity
const { count, message } = toRefs(state)
count.value++ // ✅ Vue tracks this change (note: .value required)
toRefs converts each property of a reactive object into a ref, allowing destructuring while preserving the reactive connection to the original object.
reactive() vs ref()
Choose based on what you’re making reactive.
// Use ref() for single values and primitives
const count = ref(0)
const name = ref('')
const isVisible = ref(false)
// Use reactive() for objects with multiple related properties
const pagination = reactive({ page: 1, limit: 10, total: 0 })
const filters = reactive({ search: '', category: '', sortBy: 'name' })
ref() is more versatile — it works with any type including objects. reactive() only works with objects (not primitives) and is more ergonomic when you have several related properties that logically belong together.
Best Practice Note
In CoreUI Vue components we primarily use ref() for consistency — it works with all types and the .value access makes reactivity explicit. Reserve reactive() for cases where a group of related properties genuinely benefit from direct property access, such as large form objects. See how to use ref() in Vue 3 to understand the complementary approach and when each is the better choice.



