Vue 3 gives you two ways to make something reactive: ref() and reactive(). The docs explain the difference clearly enough. What they don't tell you is which one to reach for by default.
The short answer: ref(), almost always. reactive() for specific situations where you know what you're trading away.
Here's the problem with reactive(). It loses reactivity when you destructure it or reassign the root object, which catches people off guard more than it should.
const state = reactive({ count: 0, name: '' });
state.count++; // works, Vue tracks this
const { count } = state;
count++; // doesn't work, Vue has no idea
ref() sidesteps this because the reactive pointer lives on the .value property. You can pass a ref anywhere, destructure the container, and the ref itself stays tracked.
The case where reactive() actually earns its place: a form object where all the values are always read and written together, passed as-is to a composable, and you're never going to replace the whole reference. Some people find dropping the .value noise worth that constraint. I get it. But in composables especially, ref() is much easier to work with because you can return individual refs that stay reactive when the caller destructures them.
export function useCounter() {
const count = ref(0);
const increment = () => count.value++;
return { count, increment };
}
const { count, increment } = useCounter(); // both still reactive
You can't do that cleanly with reactive() — destructuring breaks the connection.
For most of the time I was figuring out the ref/reactive split, I was making it more complicated than it needed to be. Default to ref(). Reach for reactive() when you have a specific reason. That's the whole decision.
Top comments (2)
I've been using ref() for primitive types, but how do you decide when to use reactive() instead? I'd love to hear more about your approach.
Good question, but it's not really primitive vs object;
ref()handles objects fine too, Vue just wraps them inreactive()under the hood anyway. Same deep reactivity either way.Real question is whether you ever destructure or reassign the thing. If yes, use
ref()— destructuring breaksreactive(), notref(). I only reach forreactive()for stuff like a form object where everything moves together and you're never swapping out the whole thing.