Computed Properties and Watchers

beginner
12 min

Computed Properties and Watchers

As apps grow, you constantly need values derived from other state -- a filtered list, a formatted price, a validity flag. You also sometimes need to run side effects when data changes -- saving to localStorage, fetching from an API. Vue gives you two dedicated tools: computed properties for derived state and watchers for side effects. Knowing which to reach for is a hallmark of clean Vue code.

Computed Properties

A computed property is a reactive value calculated from other reactive values. It updates automatically and is cached: it only recalculates when a dependency changes.

vue
<script setup> import { ref, computed } from 'vue' const items = ref([ { name: 'Keyboard', price: 45, qty: 1 }, { name: 'Mouse', price: 25, qty: 2 } ]) const total = computed(() => items.value.reduce((sum, i) => sum + i.price * i.qty, 0) ) const formattedTotal = computed(() => '$' + total.value.toFixed(2)) </script> <template> <p>Order total: {{ formattedTotal }}</p> </template>

Change any price or quantity and total recomputes; render the page a hundred times without changes and the function runs zero extra times. Contrast that with calling a method in the template, which re-runs on every render.

Rules for good computeds

  • They should be pure: no side effects, no async, no mutating other state.
  • Return a value; do not modify items.value inside one.
  • Chain them freely -- formattedTotal depending on total is idiomatic.

Writable computeds

Occasionally you want two-way derived state, such as binding v-model to a full name:

js
const first = ref('Ada') const last = ref('Lovelace') const fullName = computed({ get: () => first.value + ' ' + last.value, set: (val) => { [first.value, last.value] = val.split(' ') } })

Watchers

A watcher runs a callback whenever a source changes -- perfect for effects that live outside the rendering world:

js
import { ref, watch } from 'vue' const query = ref('') watch(query, async (newVal, oldVal) => { if (newVal.length < 3) return const res = await fetch('/api/search?q=' + encodeURIComponent(newVal)) console.log(await res.json()) })

watch gives you both the new and old values, supports async callbacks, and can watch multiple sources at once: watch([a, b], ([newA, newB]) => { ... }).

Useful options

js
watch(user, onChange, { deep: true }) // react to nested mutations watch(theme, apply, { immediate: true }) // also run once right away
  • deep: true is needed when watching a reactive object replaced property-by-property (watching a ref of an object whose .value is swapped does not need it).
  • immediate: true fires the callback at setup time with the current value.

watchEffect

watchEffect runs immediately and automatically tracks whatever reactive values it reads -- no explicit source list:

js
import { watchEffect } from 'vue' watchEffect(() => { document.title = query.value ? 'Searching: ' + query.value : 'My App' })

Use watchEffect for simple "keep X in sync with Y" effects; use watch when you need old values, lazy execution, or precise control over what triggers it.

Computed vs Watcher: The Decision Rule

Ask: am I producing a value, or performing an action?

| Need | Use | |---|---| | A filtered/sorted list, a formatted string, a boolean flag | computed | | Save to localStorage, call an API, start a timer, log analytics | watch / watchEffect |

A common beginner mistake is using a watcher to copy data into another ref (watch(first, () => full.value = first.value + last.value)). That is derived state -- a computed does it in one line, cached, with no ordering bugs.

Key Takeaways

  • computed creates cached, reactive derived values; keep them pure.
  • Methods in templates re-run every render; computeds only when dependencies change.
  • watch runs side effects with access to old and new values; add deep or immediate as needed.
  • watchEffect auto-tracks dependencies and runs immediately.
  • Derive values with computeds; perform actions with watchers.

Next up: Components and Props -- splitting your UI into reusable, composable pieces.

Computed Properties and Watchers - Vue.js | CodeYourCraft | CodeYourCraft