Computed or Watch?
Vue’s reactivity system has two core players: computed and watch. Both respond to data changes, but they don’t do the same job. Using the wrong one can cause unnecessary renders or unexpected behavior.
This post explains their differences, the right usage scenarios, and performance considerations.
Computed: Reactive Calculation Field
computed is for creating derived state—results that are automatically computed from one or more reactive sources.import { ref, computed } from 'vue'
const firstName = ref('Cansu')
const lastName = ref('Arı')
const fullName = computed(() => `${firstName.value} ${lastName.value}`)
Here, fullName updates whenever firstName or lastName changes. Vue tracks the computed function and recalculates only when dependencies change; otherwise it uses the cache.
Summary: Use computed when you’ll display a value in the DOM that comes from a calculation you control.
Watch: Side-Effect Listener
watch observes one or more reactive sources and runs a callback when they change. The goal isn’t to compute, but to react.import { ref, watch } from 'vue'
const age = ref(20)
watch(age, (newVal, oldVal) => {
console.log(`Age changed: ${oldVal} → ${newVal}`)
if (newVal >= 35) alert('You’re not that young anymore 😛')
})
This is handy for API calls, updating localStorage, or kicking off an animation.
Warning: Don’t use watch to provide a value for the template. A watcher doesn’t return a value; it defines a reaction.
Key Differences
| Property | Computed | Watch |
|---|---|---|
| Purpose | Compute derived value | Run side effects |
| Return value | Yes (via return) | No (callback-based) |
| Caching | Yes | No |
| Triggers re-render? | Indirectly, via its value | No; you act manually |
| Typical use | fullName, filteredList | API calls, logging, sync |
Advanced: watchEffect
Introduced in Vue 3, watchEffect sits between computed and watch. It automatically tracks any reactive values used inside.import { ref, watchEffect } from 'vue'
const count = ref(0)
watchEffect(() => {
console.log('Count changed:', count.value)
})
If you want reactive tracking without specifying dependencies, watchEffect is great. But because it can run after each render, use it carefully.
It’s typically used for data loading or “auto-reaction” flows that need cleanup.
watch vs watchEffect
| Property | watch | watchEffect |
|---|---|---|
| Dependencies | Manual | Auto-detected |
| First run | On change | Immediately |
| Performance | More controlled | Can trigger more often |
| Use case | API, side effects | Simple dependency tracking |
Performance & Best Practices
- Never perform side effects (like fetch) inside computed; computed is only for producing values.
watchmay fire frequently—use debounce/throttle where needed.- To deeply observe a reactive object, use
deep: true:
watch(() => user.value, handler, { deep: true })
Only use deep watching when necessary—it can hurt performance.Which One When?
| Scenario | Use |
|---|---|
| Derived value to display in the template | computed |
| Calculation tied to user typing | computed |
| Making an API call | watch or watchEffect |
| Syncing with localStorage | watch |
| Triggering animation/scroll on reactive change | watch |
| Simple console.log or transient reaction | watchEffect |
Real-World Example
A filtering scenario:const query = ref('')
const items = ref(['vue', 'react', 'svelte'])
const filtered = computed(() => items.value.filter(i => i.includes(query.value)))
computed drives what the DOM shows. But if you must call an API based on that change:watch(query, debounce(fetchResults, 500))
That’s where watch comes in. One controls what the UI shows; the other controls what happens outside the UI.Conclusion
The difference betweencomputed and watch goes deeper than it seems. computed computes a value, while watch reacts to a change. Use them correctly to avoid unnecessary renders and to keep your code readable.In short: computed transforms data; watch transforms behavior.