How to Watch Deep Data Structures in Vue (Arrays and Objects)

You have an array or an object as a prop, and you want your app to do something whenever that data changes.

So you create a watcher for that property, but Vue doesn't seem to fire the watcher when the nested data changes.

Here's how you solve this.

You need to set deep to true when watching an array or object so that Vue knows that it should watch the nested data for changes.

I'll go into more detail on what this looks like in this article, plus some other useful things to know when using watch in Vue.

What we'll cover in this article

First we'll do a quick refresher on what a watcher actually is.

Then we'll dive into how you can watch nested data in arrays and objects.

What is a watcher?

In Vue we can watch for when a property changes, and then do something in response to that change.

For example, if the prop colour changes, we can decide to log something to the console:

import { watch } from 'vue';
const props = defineProps({
colour: String
});
watch(props.colour, () => {
console.log(`The colour has changed to ${props.colour}!`);
});

If we want the watcher to be run immediately, or if we don't want to specifically tell Vue which values to track, we can use watchEffect instead:

import { watchEffect } from 'vue';
const props = defineProps({
colour: String
});
watchEffect(() => {
console.log(`The colour has changed to ${props.colour}!`);
});

These watchers let us do all sorts of useful things.

But many times we use a watcher when all we needed was a computed ref.

Watching nested data — Arrays and Objects

You're watching an array or an object, and it isn't working as you had expected.

What's going on here?

Let's say you set up an array with some values in it:

const array = [1, 2, 3, 4];
// array = [1, 2, 3, 4]

Now you update the array by pushing some more values into it:

array.push(5);
array.push(6);
array.push(7);
// array = [1, 2, 3, 4, 5, 6, 7]

Here's the question: has the array changed?

Well, it's not that simple.

The contents of array have changed, but the variable array still points to the same Array object. The array container hasn't changed, but what is inside of the array has changed.

So when you watch an array or an object, Vue has no idea that you've changed what's inside that prop. You have to tell Vue that you want it to inspect inside of the prop when watching for changes.

You can do this by setting deep to true on your watcher:

import { watch } from 'vue';
const props = defineProps({
colours: Array
});
watch(
() => props.colours,
() => {
console.log(`The colour has changed to ${props.colour}!`);
},
{
deep: true,
}
);

Now Vue knows that it should also keep track of what's inside the prop when it's trying to detect changes.

By default, a watcher will try to watch things deeply if it can. But it's good to know this option exists if things aren't working the way you were expecting.