LearnAI ToolsCareerPractice BuildsPlayContact
Lesson 1316 min read

Watchers

Run side effects in response to reactive state changes using watch() and watchEffect().

Why Watchers Exist

Computed properties are for deriving a value. Watchers are for running a side effect — an API call, a console log, updating localStorage — when a reactive value changes. If you find yourself wanting a computed property that also "does something," you actually want a watcher.

watch()

watch() takes a reactive source and a callback, which runs whenever that source changes, receiving both the new and old value.

<script setup>
import { ref, watch } from 'vue';
const searchTerm = ref('');
watch(searchTerm, (newValue, oldValue) => {
console.log(`Search changed from "${oldValue}" to "${newValue}"`);
fetchResults(newValue);
});
</script>

Watching Multiple Sources

Pass an array of sources to react to any of several values changing.

watch([firstName, lastName], ([newFirst, newLast]) => {
console.log('Name changed to', newFirst, newLast);
});

Deep Watching

By default, watching a reactive object only triggers on reassignment of the whole object, not changes to its nested properties. Pass { deep: true } to also react to nested mutations.

const user = reactive({ profile: { name: 'Ada' } });
watch(user, () => {
console.log('Something inside user changed');
}, { deep: true });
Deep Watching Has a Cost

deep: true recursively tracks every nested property, which can be expensive for large objects — watch a more specific, smaller piece of state when you can, instead of deep-watching a huge object.

watchEffect()

watchEffect() runs immediately, and automatically tracks whatever reactive values it reads inside — you don't need to explicitly list the source(s) up front.

import { ref, watchEffect } from 'vue';
const id = ref(1);
watchEffect(() => {
console.log('Fetching data for user', id.value); // runs immediately, then again whenever id changes
});

watch vs watchEffect vs computed

computedwatchwatchEffect
PurposeDerive a valueReact to specific changes with a side effectReact to any tracked change with a side effect
Returns a value?YesNoNo
Runs immediately?Lazily, on first accessNo, only after a change (by default)Yes, immediately
DependenciesAutomatically trackedExplicitly listedAutomatically tracked

FAQs

Yes — pass { immediate: true } as a third argument to watch() to run the callback once right away, in addition to on every change.

Both watch() and watchEffect() return a stop function you can call — but in a component's setup, they're automatically stopped when the component unmounts, so this is rarely needed manually.

Summary

Reach for computed() to derive a value, and watch()/watchEffect() to run a side effect in response to change. Next, you'll learn how to bind classes and inline styles dynamically.

Next Lesson →

Class and Style Binding