diagram.mmd — flowchart
Vue Reactivity System flowchart diagram

Vue's reactivity system is the mechanism by which Vue automatically tracks which reactive data a component reads during render and re-renders only when that data changes — without any explicit subscription wiring from the developer.

Vue 3's reactivity is built on ES6 Proxy objects. When you call reactive({ count: 0 }) or ref(0), Vue wraps the value in a Proxy that intercepts get and set traps. During any reactive computation — rendering a component, evaluating a computed property, or running a watchEffect — Vue activates a global active effect tracker. Every get trap fires the track() function, which registers the current active effect as a subscriber of that specific property. This is dependency tracking, and it happens automatically just by reading the value.

When a reactive property is mutated (state.count++), the Proxy fires the set trap, which calls trigger(). trigger() looks up the subscriber list for that property and schedules all registered effects to re-run. Component render effects are scheduled on the microtask queue via Promise.resolve() to batch multiple synchronous mutations into a single re-render. This is why nextTick() resolves after all pending DOM updates — it resolves after the flushed microtask queue.

computed properties are lazy — they re-evaluate only when accessed and only when their tracked dependencies have changed since the last evaluation. watch and watchEffect run effects with configurable flush timing: flush: 'pre' (before DOM update), flush: 'post' (after DOM update, useful for DOM access), or flush: 'sync' (immediately on mutation, rare). This contrasts with React's model where React Rendering Lifecycle is explicit about scheduling via hooks. Vue's proxy-based approach means no need for manual dependency arrays — but it requires reactive values to remain accessed through the proxy (destructuring breaks reactivity unless you use toRefs).

Understanding this system explains the toRef, toRefs, storeToRefs, and shallowReactive APIs that preserve or limit reactive proxy wrapping.

Free online editor
Edit this diagram in Graphlet
Fork, modify, and export to SVG or PNG. No sign-up required.
Open in Graphlet →

Frequently asked questions

Vue's reactivity system is the mechanism that automatically tracks which reactive data a component reads during render and re-renders only that component when those values change. In Vue 3 it is built on ES6 Proxy objects, which intercept property reads (`get`) to track dependencies and property writes (`set`) to schedule updates.
When you access a property on a `reactive()` or `ref()` object inside a computed, watcher, or component render function, Vue's active effect tracker records that property as a dependency of the current effect. When the property is later mutated, the Proxy's `set` trap calls `trigger()`, which schedules all registered subscriber effects to re-run on the next microtask flush, batching multiple synchronous mutations into a single re-render.
Destructuring a reactive object (`const { count } = state`) copies the primitive value at that moment, severing the Proxy connection. The variable `count` is just a number — it will not update when `state.count` changes. Fix this with `toRefs(state)` to convert each property to an individual `ref` that maintains the reactive link, or access properties directly through the reactive object.
Vue's Proxy-based reactivity tracks dependencies automatically at the property level — the system knows exactly which components read which data without any explicit subscription code. React's `useState` requires explicit hook calls and re-renders the entire component subtree below the state owner unless `React.memo` or `useMemo` are used to bail out. Vue's model typically results in more granular re-renders by default; React's model is more explicit and predictable about when work occurs.
mermaid
flowchart TD A[Create reactive or ref value] --> B[Wrap in ES6 Proxy] B --> C[Component Render Effect Activated] C --> D[Read Reactive Property — get trap fires] D --> E[track — Register Effect as Subscriber] E --> F[Render Output Produced] F --> G[User or Code Mutates Reactive Value] G --> H[Proxy set trap fires] H --> I[trigger — Look up Subscribers] I --> J[Schedule Render Effect on Microtask Queue] J --> K{More mutations in same tick?} K -- Yes --> L[Queue deduplicates — single re-render] K -- No --> M[Flush Microtask Queue] L --> M M --> N[Re-run Render Effect] N --> O[Reconcile Diff and Patch DOM] O --> P[nextTick resolves] style P fill:#22c55e,color:#fff
Copied to clipboard