Vue 3's reactivity is what makes the UI update automatically when data changes, and most of the time it just works. The trouble comes when it silently stops — you change a value and nothing re-renders — and without understanding how the system works, that is baffling. A little knowledge of the Proxy-based mechanism turns those mysteries into predictable, avoidable mistakes.
Proxy-based tracking
Vue 3 rebuilt reactivity on JavaScript Proxies. When you make an object reactive with reactive(), Vue wraps it in a Proxy that intercepts property reads and writes. On read, it tracks which effect (a render, a computed, a watcher) depends on that property; on write, it triggers those effects to re-run. This is why reactivity feels automatic — the Proxy is watching every access. It is also why the gotchas exist: they are all cases where you accidentally step outside the Proxy's view.
Why ref needs .value
JavaScript primitives (numbers, strings) cannot be proxied directly, so Vue wraps them in a ref object whose .value property can be tracked. That is the whole reason for the .value you write in script code. In templates Vue unwraps refs automatically, which is why you omit .value there. reactive() is for objects and needs no .value; ref() is for anything and always does in script.
Where reactivity breaks
- Destructuring a reactive object — pulling out a property copies the value and loses the Proxy connection; use
toRefs()to keep each property reactive. - Replacing a reactive object wholesale — reassigning the variable breaks the link; mutate its properties or use a ref instead.
- Adding deeply nested properties — usually fine in Vue 3's Proxy system, unlike Vue 2, but be deliberate with large nested structures.
import { reactive, toRefs } from 'vue'
const state = reactive({ x: 0, y: 0 })
const { x, y } = toRefs(state) // x and y stay reactive refs
// const { x } = state // x is a plain number — NOT reactive
When the UI stops updating, you have almost certainly severed the Proxy link — by destructuring, reassigning, or passing a plain value where a reactive one was expected. Trace where the value left the reactive system, and use toRefs or keep the reference intact. Reactivity does not fail randomly; it fails where you stepped outside the Proxy.
Vue 3 reactivity is a Proxy system that tracks reads and triggers on writes, with ref wrapping primitives so their .value can be watched. Every classic gotcha — lost updates after destructuring or reassignment — is a case of breaking that Proxy connection. Understand the mechanism and reactivity stops being magic that mysteriously fails and becomes a system whose rules you can follow.
Independent software engineer in Nairobi specialising in Acumatica customisations, Laravel backends, and tax fiscalisation integrations across East and Southern Africa.