Tutorials Logic, IN info@tutorialslogic.com

Vue Reactivity ref, reactive, computed, watch

Reactive Dependency Tracking

Vue tracks reactive values read while an effect runs and triggers that effect when those dependencies change. ref is the straightforward choice for a primitive or replaceable value; reactive is convenient for an object whose properties are mutated. Computed values derive cached state and should remain free of side effects.

Vue's Reactivity System

Vue's reactivity system automatically tracks data dependencies and updates the DOM when data changes. In Vue 3's Composition API, you declare reactive state using ref() and reactive().

API Use for Access value
ref() Primitives (string, number, boolean) and any value count.value in JS, {{ count }} in template
reactive() Objects and arrays state.name directly (no .value)
computed() Derived values - auto-updates when deps change Like ref - .value in JS
watch() Side effects when data changes Callback receives new/old value
watchEffect() Auto-track dependencies, run immediately No explicit deps needed

ref, reactive, computed, watch - Complete Examples

ref, reactive, computed, watch - Complete Examples
<template>
  <div>
    <!-- ref values -->
    <p>Count: {{ count }}</p>
    <p>Name: {{ name }}</p>
    <button @click="count++">+1</button>

    <!-- reactive object -->
    <p>{{ user.name }} ({{ user.age }})</p>
    <button @click="user.age++">Birthday</button>

    <!-- computed -->
    <p>Full name: {{ fullName }}</p>
    <p>Double: {{ double }}</p>
    <p>Filtered: {{ filteredItems.length }} items</p>
  </div>
</template>

<script setup>
import { ref, reactive, computed } from 'vue'

// ref - for primitives
const count = ref(0)
const name  = ref('Alice')

// reactive - for objects
const user = reactive({
  firstName: 'Alice',
  lastName: 'Smith',
  age: 25
})

const items = reactive([
  { id: 1, name: 'Apple',  active: true },
  { id: 2, name: 'Banana', active: false },
  { id: 3, name: 'Cherry', active: true },
])

// computed - derived, cached, auto-updates
const fullName = computed(() => `${user.firstName} ${user.lastName}`)
const double   = computed(() => count.value * 2)
const filteredItems = computed(() => items.filter(i => i.active))

// Writable computed
const firstName = ref('Alice')
const lastName  = ref('Smith')
const fullNameWritable = computed({
  get: () => `${firstName.value} ${lastName.value}`,
  set: (val) => {
    const parts = val.split(' ')
    firstName.value = parts[0]
    lastName.value  = parts[1] || ''
  }
})
</script>

Vue's Reactivity System - HTML Example

Vue's Reactivity System - HTML Example
<template>
  <div>
    <input v-model="searchQuery" placeholder="Search..." />
    <p>Results: {{ results.length }}</p>
    <p>User: {{ user.name }}</p>
  </div>
</template>

<script setup>
import { ref, reactive, watch, watchEffect } from 'vue'

const searchQuery = ref('')
const results = ref([])
const user = reactive({ name: 'Alice', age: 25 })

// watch - explicit source, runs when it changes
watch(searchQuery, async (newVal, oldVal) => {
  console.log(`Changed from "${oldVal}" to "${newVal}"`)
  if (newVal.length > 2) {
    results.value = await fetchResults(newVal)
  }
})

// watch with options
watch(searchQuery, (newVal) => {
  console.log('Debounced search:', newVal)
}, {
  immediate: true,  // run immediately on mount
  deep: false       // don't watch nested properties
})

// Watch reactive object - need deep: true for nested changes
watch(user, (newUser) => {
  console.log('User changed:', newUser)
}, { deep: true })

// Watch specific property of reactive object
watch(() => user.age, (newAge) => {
  console.log('Age changed to:', newAge)
})

// watchEffect - auto-tracks dependencies, runs immediately
watchEffect(() => {
  // Automatically tracks searchQuery.value
  console.log('Query is now:', searchQuery.value)
  document.title = `Search: ${searchQuery.value}`
})

// Stop a watcher
const stop = watchEffect(() => { /* ... */ })
// stop()  // call to stop watching

async function fetchResults(query) {
  const res = await fetch(`/api/search?q=${query}`)
  return res.json()
}
</script>

Preserving Reactivity

Destructuring a property from a reactive object into an ordinary variable disconnects that local variable from future updates. Use toRefs when destructuring is needed, or read through the reactive object. Replacing an entire reactive object also breaks references held elsewhere; use a ref when whole-object replacement is normal.

Watch a source when an external side effect must follow a change, such as saving a draft or requesting data. Use computed for values that can be derived synchronously. Clean up obsolete watcher work and avoid deep watching large structures when a specific field or normalized key expresses the dependency.

Before you move on

Vue Reactivity ref, reactive, computed, watch Mastery Check

4 checks
  • Choose ref or reactive from the value shape and access pattern, then use .value only where required.
  • Preserve tracking when destructuring reactive state by using refs or toRefs where appropriate.
  • Use computed for pure derived state and watchers only for effects with explicit cleanup.
  • Test nested updates, replacement, template unwrapping, nextTick timing, and non-reactive external objects.
Browse Free Tutorials

Explore 500+ free tutorials across 20+ languages and frameworks.