What does `reactive()` do in Vue 3?
1. What reactive() does
The reactive() function takes a plain object and returns its reactive (proxied) version.
import { reactive } from 'vue'
const state = reactive({
count: 0,
user: { name: 'Tim' }
})Now, when you change any property of the object:
state.count++
state.user.name = 'Alex'Vue automatically tracks these changes and updates all the effects, computed values, or components that depend on them.
2. How it works under the hood
Under the hood, reactive() creates a Proxy wrapper around the object you pass in.
Simplified:
function reactive(target) {
return new Proxy(target, {
get(target, key, receiver) {
// register the dependency when the value is read
track(target, key)
const res = Reflect.get(target, key, receiver)
// if the value is an object, make it reactive recursively
if (typeof res === 'object' && res !== null) {
return reactive(res)
}
return res
},
set(target, key, value, receiver) {
const oldValue = target[key]
const result = Reflect.set(target, key, value, receiver)
// if the value actually changed, trigger updates
if (oldValue !== value) {
trigger(target, key)
}
return result
}
})
}track() remembers that the current computation (effect/render) depends on this property.
trigger() notifies all effects that the property changed.
3. Example "in action"
import { reactive, effect } from 'vue'
const state = reactive({ count: 0 })
effect(() => {
console.log(`Count: ${state.count}`)
})
state.count++ // "Count: 1"- On the first call to
effect(), Vue readsstate.count-> callingtrack(). - Vue "remembers": this function depends on
state.count. - When
state.countchanges ->trigger()rerunseffect(). - The console prints the new value.
4. What reactive() does internally
| Step | What Vue does |
|---|---|
| 1. Takes an object | Checks whether it is already reactive |
| 2. Creates a Proxy | Intercepts get and set |
3. On get -> track() | Registers the dependency |
4. On set -> trigger() | Notifies all dependent effects |
| 5. Returns the wrapper | Which behaves like the original object but "reacts" to changes |
5. Difference from ref()
| Characteristic | reactive() | ref() |
|---|---|---|
| Data type | Wraps an object | Wraps a primitive (number, string, etc.) |
| Access | Direct access (state.count) | Needs .value (count.value) |
| Nested objects | Automatically become reactive | Must be wrapped manually |
| When to use | For complex state | For simple values |
Example:
const state = reactive({ count: 0 })
const count = ref(0)
state.count++ // reactive
count.value++ // reactive6. Important details
reactive()only works with objects If you pass a primitive (number, string, etc.), Vue just returns it as is:
reactive(10) // returns 10, not reactive- Reactivity is applied lazily (deep reactive) Nested objects become reactive only on first access.
- Comparison by reference
Since it's a Proxy,
state !== rawObject:
const raw = { x: 1 }
const proxy = reactive(raw)
console.log(proxy === raw) // false- You can get the original with
toRaw():
import { toRaw } from 'vue'
const original = toRaw(state)- You can "freeze" an object against reactivity with
markRaw():
import { markRaw } from 'vue'
const nonReactive = markRaw({ foo: 'bar' })7. When to use reactive()
-
For component state objects:
javascriptconst form = reactive({ name: '', email: '', accepted: false }) -
For complex structures (nested objects, arrays, collections):
javascriptconst data = reactive({ users: [{ id: 1 }, { id: 2 }], meta: { total: 2 } }) -
For centralized state stores (for example, without Vuex / Pinia):
javascriptexport const store = reactive({ user: null, isAuth: false })
8. Summary
reactive()makes a plain object reactive using a Proxy. Vue automatically tracks all reads and writes to its properties. Any effects, templates, or computed values that depend on these properties update automatically when the data changes.
Short Answer
Interview readyA concise answer to help you respond confidently on this topic during an interview.