What Causes This Warning?
This warning occurs when you try to directly modify a prop value inside a child component. In Vue.js, props follow a one-way data flow - data flows down from parent to child, and props are readonly in the child component.
The Problem
<script setup>
const props = defineProps(['count'])
// ❌ Directly mutating prop
function increment() {
props.count++ // Warning: Attempting to mutate prop "count"
}
</script>
The Fix
Option 1: Emit an Event to Parent
<!-- Child Component -->
<script setup>
const props = defineProps(['count'])
const emit = defineEmits(['update:count'])
// ✅ Emit event to parent
function increment() {
emit('update:count', props.count + 1)
}
</script>
<!-- Parent Component -->
<ChildComponent :count="count" @update:count="count = $event" />
Option 2: Use v-model (Vue 3.4+)
<!-- Child Component -->
<script setup>
const count = defineModel('count')
// ✅ Modify the model directly
function increment() {
count.value++
}
</script>
<!-- Parent Component -->
<ChildComponent v-model:count="count" />
Option 3: Use Local State
<script setup>
const props = defineProps(['initialCount'])
// ✅ Create local copy
const localCount = ref(props.initialCount)
function increment() {
localCount.value++
}
</script>
Option 4: Use Computed with Setter
<script setup>
const props = defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])
// ✅ Computed property with setter
const value = computed({
get: () => props.modelValue,
set: (val) => emit('update:modelValue', val)
})
</script>
Common Scenarios
Form Input Binding
<!-- ❌ Wrong -->
<script setup>
const props = defineProps(['username'])
</script>
<template>
<input v-model="username" /> <!-- Mutates prop! -->
</template>
<!-- ✅ Correct -->
<script setup>
const props = defineProps(['username'])
const emit = defineEmits(['update:username'])
const localUsername = computed({
get: () => props.username,
set: (val) => emit('update:username', val)
})
</script>
<template>
<input v-model="localUsername" />
</template>
Array/Object Props
<!-- ❌ Wrong - mutating object prop -->
<script setup>
const props = defineProps(['user'])
function updateName(name) {
props.user.name = name // Mutates prop object!
}
</script>
<!-- ✅ Correct - emit full new object -->
<script setup>
const props = defineProps(['user'])
const emit = defineEmits(['update:user'])
function updateName(name) {
emit('update:user', { ...props.user, name })
}
</script>
Why Props Are Readonly
- Predictable data flow: Makes debugging easier
- Single source of truth: Parent owns the data
- Prevents side effects: Child can’t unexpectedly change parent state
- Better component design: Forces explicit communication
Quick Checklist
- Never directly assign to
props.x - Use
emit()to notify parent of changes - Consider
defineModel()for two-way binding - Use local
ref()if you need a mutable copy - Remember object/array props can still be mutated (avoid this!)