Vue's reactivity system is what makes your UI update automatically when data changes. This lesson explains how it works, when to use ref vs reactive, and how to derive state with computed properties and respond to changes with watchers.
Vue tracks which reactive variables your template uses. When any of them change, Vue re-renders just the parts of the DOM that depend on that data. You don't call setState() or manually trigger updates — Vue handles it automatically.
There are two ways to create reactive state: ref() and reactive(). They behave differently and are suited to different situations.
Use ref() for single values — strings, numbers, booleans, or even objects when you want to replace the whole thing. Use reactive() for objects and arrays when you'll only ever mutate their properties, never replace the whole object.
import { ref, reactive } from 'vue'
// ref: good for primitives and single values
const count = ref(0)
const username = ref('alice')
const isLoggedIn = ref(false)
// Access with .value in
Full name: {{ fullName }}
Total: ${{ totalPrice }}
Sometimes you need to run code in response to data changes — like calling an API when a search term updates, or saving to localStorage when a setting changes. That's what watch and watchEffect are for.
import { ref, watch, watchEffect } from 'vue'
const searchQuery = ref('')
const results = ref([])
// watch: explicit source, fires when source changes
// Gets old and new values
watch(searchQuery, async (newVal, oldVal) => {
if (newVal.length > 2) {
results.value = await fetchResults(newVal)
}
})
// watch with options
watch(searchQuery, handler, {
immediate: true, // run once on mount
deep: true // watch nested object changes
})
// watchEffect: auto-tracks dependencies
// Runs immediately and whenever any reactive value it reads changes
watchEffect(() => {
console.log('Search changed to:', searchQuery.value)
// Vue automatically knows this depends on searchQuery
})
// Cleanup: stop watching when component unmounts
const stop = watchEffect(() => { /* ... */ })
// stop() when you're done
watch, that's a signal to use computed instead.ref search query.computed to filter the array based on the search query.computed property.watch to log every search query change to the console with a debounce of 300ms.ref() is for single values. Access with .value in scripts, auto-unwrapped in templates.reactive() is for objects. Access properties directly. Never replace the whole reactive object.computed() caches derived values. Only recalculates when dependencies change.watch() runs side effects when specific data changes. Gets old and new values.watchEffect() auto-tracks its reactive dependencies and runs immediately.