Tutorials Logic, IN +91 8092939553 info@tutorialslogic.com
FAQs Support
Navigation
Home About Us Contact Us Blogs FAQs
Tutorials
All Tutorials
Services
Academic Projects Resume Writing Interview Questions Website Development
Compiler Tutorials

Vue Reactivity — ref, reactive, computed, watch

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().

APIUse forAccess value
ref()Primitives (string, number, boolean) and any valuecount.value in JS, {{ count }} in template
reactive()Objects and arraysstate.name directly (no .value)
computed()Derived values — auto-updates when deps changeLike ref — .value in JS
watch()Side effects when data changesCallback receives new/old value
watchEffect()Auto-track dependencies, run immediatelyNo explicit deps needed
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>
<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>

Ready to Level Up Your Skills?

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