What does watch() do?
1. What watch() does
watch() watches for changes in reactive values (created via ref, reactive, computed, etc.)
and runs the given function only when those values actually change.
Syntax:
watch(source, callback, options?)source- what we watch (ref,reactive, a function, or an array).callback(newValue, oldValue)- called on change.options- additional settings (immediate,deep,flush, etc).
2. The simplest example
import { ref, watch } from 'vue'
const count = ref(0)
watch(count, (newVal, oldVal) => {
console.log(`count changed: ${oldVal} → ${newVal}`)
})
count.value++ // triggers the callbackHere watch() "subscribes" to changes in count.value.
When the value changes, the callback is called.
3. How it works internally
Under the hood, watch() creates an effect, just like computed, but with "only on change" behavior.
- Vue remembers which reactive data were used in the source function (
source). - When that data changes, Vue calls the callback.
- Vue compares the old and new value (a shallow comparison).
- If the value changed, your logic runs.
4. Example with a reactive object
import { reactive, watch } from 'vue'
const user = reactive({
name: 'Tim',
age: 25
})
// Watch the whole object
watch(user, (newUser, oldUser) => {
console.log('user changed:', newUser)
})But note:
When watching a
reactiveobject, changes inside the object do not trigger the callback by default, unless you specify{ deep: true }.
Correct:
watch(user, (newVal, oldVal) => {
console.log('user changed deeply:', newVal)
}, { deep: true })5. Watching a specific property
Instead of the whole object, you can pass a function, a "selector".
watch(
() => user.name, // watch only name
(newName, oldName) => {
console.log(`Name changed: ${oldName} → ${newName}`)
}
)This is the preferred way - it is performant and targeted.
6. watch() options
| Option | Description | Example |
|---|---|---|
immediate: true | Run the callback immediately on initialization (with the current value) | watch(count, cb, { immediate: true }) |
deep: true | Watch all nested changes in an object/array | watch(user, cb, { deep: true }) |
flush | Determines when the watcher runs relative to the render. | watch(value, cb, { flush: 'post' }) |
onCleanup(fn) | Lets you clean up a side effect before the next call | see below |
7. Cleaning up side effects
Often, watch() needs to clean up timers, subscriptions, and so on.
Vue passes an onCleanup function into the callback so you can clean up the previous effect:
watch(searchQuery, (newQuery, oldQuery, onCleanup) => {
const controller = new AbortController()
fetch(`/api?q=${newQuery}`, { signal: controller.signal })
// cancel the previous request if the value changed again
onCleanup(() => controller.abort())
})8. Watching several sources
You can watch several values at once by passing an array:
const first = ref('Tim')
const last = ref('Cook')
watch([first, last], ([newFirst, newLast], [oldFirst, oldLast]) => {
console.log(`Name: ${oldFirst} ${oldLast} → ${newFirst} ${newLast}`)
})Vue tracks all the dependencies, and the callback is called if at least one of them changes.
9. Difference between watch() and watchEffect()
| Criterion | watch() | watchEffect() |
|---|---|---|
| What it tracks | Specific reactive values or functions | Everything used inside the effect |
| When it runs | Only when values change | Immediately on start and on every change |
Has oldValue | Yes | No |
| Fine-grained control | Yes (you can specify deep, immediate) | No, everything is automatic |
| Typical case | React to a state change | Run side effects on any change |
Example:
// watch()
watch(count, (newVal, oldVal) => console.log(newVal))
// watchEffect()
watchEffect(() => {
console.log(count.value)
})10. A practical example
For example, you want to react to a change in the search string, but not on every render:
const query = ref('')
watch(query, async (newQuery) => {
results.value = await fetch(`/api?q=${newQuery}`).then(r => r.json())
}, { immediate: true })Here immediate: true gives the first request when the component mounts,
and after that watch() only triggers when query changes.
11. Short and simple
watch()is a "reactive listener": it tracks specific data and calls a function when it changes.
You use it when you need to perform an action, not just update the UI.
An example to remember
| What you want | What to use |
|---|---|
| I want the UI to update itself when the data changes | reactive() / ref() / computed() |
| I want to run a side effect when the data changes (e.g. an API call, a log, a timer, local storage) | watch() |
Short Answer
Interview readyA concise answer to help you respond confidently on this topic during an interview.