0Pricing
Frontend Academy · Lesson

computed() and watch()

Derive values lazily with computed(), react to state changes with watch() and watchEffect(), and control flush timing.

computed() and watch() is a free Frontend Academy lesson on CoddyKit — lesson 2 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Frontend Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.

computed() in Composition API

computed(fn) creates a read-only reactive ref that derives its value from other reactive sources. It's cached — the getter only runs when dependencies change.

<script setup>
import { ref, computed } from 'vue';

const items = ref([{ price: 10 }, { price: 20 }, { price: 30 }]);
const tax = ref(0.1);

const subtotal = computed(() => items.value.reduce((s, i) => s + i.price, 0));
const total = computed(() => subtotal.value * (1 + tax.value));
</script>

Writable computed()

Pass an object with get and set to create a writable computed. The setter handles updates, typically reflecting the change back to the source ref.

const firstName = ref('Alice');
const lastName  = ref('Smith');

const fullName = computed({
  get: () => `${firstName.value} ${lastName.value}`,
  set: (val: string) => {
    [firstName.value, lastName.value] = val.split(' ');
  }
});

fullName.value = 'Bob Jones'; // sets both refs

watch() Basics

watch(source, callback) runs the callback when the source changes. The callback receives the new and old values.

import { ref, watch } from 'vue';

const query = ref('');

watch(query, (newVal, oldVal) => {
  console.log(`Changed from '${oldVal}' to '${newVal}'`);
  searchAPI(newVal);
});

Watching a ref vs reactive

Watch a ref directly. To watch a reactive property, use a getter function () => state.prop.

const count = ref(0);
watch(count, val => console.log('count:', val));

const state = reactive({ count: 0, name: '' });
watch(() => state.count, val => console.log('state.count:', val));

Deep Watch

Pass { deep: true } to watch nested changes in an object ref.

const user = ref({ name: 'Alice', address: { city: 'NY' } });

watch(user, (newUser) => {
  console.log('user changed:', newUser);
}, { deep: true });

Immediate Watch

Pass { immediate: true } to run the callback immediately on setup, before any change. Useful when you want to run a side effect with the initial value.

watch(userId, async (id) => {
  user.value = await fetchUser(id);
}, { immediate: true }); // runs once on setup, then on change

watchEffect()

watchEffect(fn) runs the callback immediately and re-runs whenever any reactive dependency used inside it changes. No need to specify sources explicitly.

import { watchEffect } from 'vue';

watchEffect(() => {
  // runs immediately and whenever query or page changes:
  fetchResults(query.value, page.value);
});

watchEffect vs watch

watchEffect: immediate, auto-tracks deps, no access to old value. watch: explicit sources, lazy by default, access to old and new values. Use watchEffect for declarative side effects; watch when you need control over when/how often it runs.

Stopping Watchers

Both watch and watchEffect return a stop function. Call it to stop watching. In