Reactive Data

Every value returned from a component's data() gets wrapped by Vue in a reactivity system that quietly tracks who reads it and re-runs the right parts of the template whenever it's written to.

How data() becomes reactive

You've already been mutating reactive data with this.count++ in the previous lesson without needing to know how it worked. Under the hood, Vue turns each property returned by data() into a getter/setter pair — reading this.count tells Vue "the template depends on this," and writing to it tells Vue "re-render anything that depends on this":

JS component
Vue.createApp({
  data() {
    return {
      count: 0,
      user: { name: 'Priya', loggedIn: true }
    }
  }
})

Both the plain number count and the nested object user are reactive — changing this.user.name updates the template exactly like changing this.count would.

The Composition API alternative: ref() and reactive()

Modern Vue code (especially inside <script setup> in single-file components) often uses ref() and reactive() instead of the data() option — same underlying reactivity system, different syntax:

JS Composition API
import { ref } from 'vue'

const count = ref(0)
console.log(count.value)   // 0

count.value++
console.log(count.value)   // 1
The .value gotcha: a ref is an object wrapper around your value — inside JavaScript code you always access or change it through .value (as above). Vue templates unwrap refs automatically, so in the template itself you'd just write {{ count }}, never {{ count.value }}. Forgetting .value in your script, or accidentally adding it inside the template, is one of the most common Composition API beginner mistakes.

Reactivity only tracks what it can see

Vue's reactivity works by intercepting property access, which has one real limitation worth knowing early: reassigning an entire reactive object (rather than a property on it) can lose reactivity if you're not using the right API for that case:

JS a mistake to avoid
// data() returns { user: {...} }
this.user = { name: 'New Name' }   // fine — this.user is reactive, reassigning it is tracked

let localCopy = this.user
localCopy = { name: 'Ignored' }    // does NOT update the template — localCopy is a plain variable now

Assigning to this.user directly is tracked because user is a property Vue is watching. Reassigning a plain local variable that merely held a reference to that object isn't — the variable itself was never reactive, only the object it originally pointed to.