The most common Vue 3 reactivity confusion is practical, not theoretical: why does a primitive value need ref, when should an object use reactive, and why does destructuring sometimes make reactivity disappear?
Once those three questions are clear, ref, reactive, and toRefs become much easier to choose.
What reactivity means in Vue 3
Reactivity means Vue can track reads and writes, then update the view when tracked data changes. Vue 3 uses JavaScript Proxy behavior for objects and wrapper objects for values that cannot be proxied directly.
Use ref for primitive values
Primitive values such as strings, numbers, and booleans are passed by value. Vue needs a wrapper it can track:
import { ref } from "vue"
const count = ref(0)
count.value = 1
Use ref for:
- counters
- booleans
- selected IDs
- input strings
- values returned from composables
In templates, Vue unwraps refs automatically. In JavaScript or TypeScript code, use .value.
Use reactive for objects
Objects and arrays are reference values, and Vue can proxy their properties:
import { reactive } from "vue"
const state = reactive({
count: 0,
user: { name: "Vue" },
})
state.count = 1
state.user.name = "Vue 3"
Use reactive when several fields belong to one state object, such as a form, panel state, or local component model.
Why destructuring can lose reactivity
Directly destructuring a reactive object breaks the tracked property access:
const state = reactive({ count: 0, name: "Vue" })
const { count, name } = state
count and name are now ordinary local values. They are not connected to state updates in the way you usually expect.
Use toRefs when you need to destructure while keeping each property connected:
import { reactive, toRefs } from "vue"
const state = reactive({ count: 0, name: "Vue" })
const { count, name } = toRefs(state)
Now count and name are refs that stay linked to the original reactive object.
What toRefs solves
toRefs converts each property of a reactive object into a ref connected to the original object.
It is useful when:
- returning state from a composable
- passing individual fields around
- destructuring for template or setup usage
It is not a reason to make every state object more complex. Use it when destructuring is actually needed.
Practical rule
| Data shape | Prefer |
|---|---|
| one primitive value | ref |
| one object with related fields | reactive |
| destructured properties from a reactive object | toRefs |
| reusable composable return values | often refs or toRefs |
Avoid wrapping a single number in reactive. Avoid making every object a ref if it forces awkward .value.deep.property access everywhere.
Related FreeMac guides
- For side effects in Vue and React, read Side Effects in Vue 3 and React.
- For object copying and references, read JavaScript Object Copy: Destructuring, Shallow Copy, and Deep Copy.
- For array behavior, read JavaScript Array Methods: map, splice, sort, and for...of.
Continue reading
Side Effects in Vue 3 and React: Watchers, Effects, and Cleanup
Compare side effect handling in Vue 3 and React, including API requests, event listeners, timers, AbortController, watchers, useEffect dependencies, and cleanup.
9 min readIntersectionObserver Guide: Lazy Loading and Scroll Triggers
Use IntersectionObserver for lazy loading, infinite scroll, reveal animations, and view tracking without constantly calculating scroll position by hand.
8 min readJavaScript var vs let: Function Scope, Block Scope, and Closures
Understand the difference between var and let in JavaScript through function scope, block scope, for loops, closures, and why let avoids the classic 3 3 3 result.
Subscribe to FreeMac
Weekly picks: free Mac software reviews, trusted source updates, alternatives, and low-friction guides.