If you are using Vue 3 + ESLint and trying to mutate a prop in your Vue component, you should see the following error:
ESLint error
Unexpected mutation of "todo" prop. eslintvue/no-mutating-props
If you are using Vue 2, Vue will throw the following error/warning in your browser's console:
Message in browser console
[Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value.
Explanation
First, we look at the official documentation to understand why Vue throws that error.
Why do we see the error
When objects and arrays are passed as props, while the child component cannot mutate the prop binding, it will be able to mutate the object or array's nested properties. This is because JavaScript objects and arrays are passed by reference, and it is unreasonably expensive for Vue to prevent such mutations.
Why is it a problem
The main drawback of such mutations is that it allows the child component to affect the parent state in a way that isn't obvious to the parent component, potentially making it more difficult to reason about the data flow in the future. As a best practice, you should avoid such mutations unless the parent and child are tightly coupled by design.
Recommended solution
In most cases, the child should emit an event to let the parent perform the mutation.
Demo
Let's look at a code example. I'm using a Todo app to demonstrate the error. The source code is interactively available on StackBlitz:
Let's start by taking a look at the TodoList.vue
component:
TodoList.vue
contains a reactive variable todos
that include an array of Todo items. In the template, each item is rendered via TodoItem.vue
component:
Assuming ESLint is correctly configured, you should see the following ESLint error if you open TodoItem.vue
in your editor:
Mutating props is an anti-pattern
We want to write components that are easy to maintain.
In a maintainable component, only the component itself should be able to change its own state.
Additionally, only the component's parent should be able to change the props.
These two rules are essential to ensure Vue's One-Way Data Flow to make our app's data flow easier to understand.
Warning
If we mutate the props, we violate both rules and break Vue's data flow!
In addition, every time the parent component is updated, all props in the child component will be refreshed with the latest value.
Solution
Info
In most cases, the error can be solved using a computed property.
In our example, instead of mutating the prop, we emit an event to the parent. The parent is then responsible for updating the Todo list correctly.
Let's use a writeable computed property in TodoItem.vue
. Its getter accesses the prop's value, and the setter emits an event to the parent:
Finally, we need to react to the emitted event in TodoList.vue
and update the todos
accordingly:
The prop is not mutated with this solution, and we correctly use the one-way data flow in our Vue application.
Info
The amazing Michael Thiessen also wrote a detailed article about this topic.
Video
If you liked this Vue tip, follow me on X to get notified about new tips, blog posts, and more. Alternatively (or additionally), you can subscribe to my weekly Vue & Nuxt newsletter:
Vue Tip: Disable Attribute Inheritance
By default, parent scope attribute bindings that are not recognized as props will "fallthrough". This may not always be the desired behavior and, therefore, can be disabled. A typical use case is a component with multiple root nodes.
Vue Tip: Pass Custom Arguments to Event Handler Method
Sometimes we need to pass custom arguments to the event handler method and access the original DOM event.