In Vue, props are meant to be read-only inputs from parent to child. Explain what breaks (correctness + debugging) when a child component directly mutates a prop, including the nested object/array case, and show the correct patterns (emit updates / v-model).
Use this Vue interview question to rehearse a quick answer, common mistake, follow-up, and production pitfall.
What breaks if a child mutates a prop directly?Frontend interview answer
This Vue interview question tests whether you can explain Happens If a Child Mutates a Prop Directly in Vue, connect it to production trade-offs, and handle common follow-up questions.
- Happens If a Child Mutates a Prop Directly in Vue explanation without falling back to memorized docs wording
- Props and Immutability reasoning, edge cases, and production failure modes
- How you would answer the most likely Vue interview follow-up
Core idea
Props are the parent’s state flowing down. If the child mutates a prop, you lose the “single source of truth” and the update path becomes ambiguous (who changed it, when, and why). Vue will warn in dev, but the bigger issue is correctness + maintainability.
What the child does | What breaks | Why it’s bad |
|---|---|---|
Assigns a prop directly (e.g. props.count++) | Vue warns; update is rejected / overwritten on next parent render | Child is trying to mutate parent-owned state without going through the parent |
Mutates a nested field on an object/array prop (e.g. props.user.name = 'x') | You silently mutate the parent’s object (shared reference) and create “action at a distance” bugs | Objects/arrays are passed by reference; child mutation becomes parent mutation |
Uses a prop as local state (editing it directly) | UI can desync (parent re-renders and resets the child), race conditions with async updates | Parent can re-send the prop anytime; child-local edits aren’t a stable source of truth |
Triggers watchers/computed based on mutated prop | Hard-to-trace update loops / unexpected re-renders | You bypass the intended data flow and can create circular updates |
Bad example: child mutates a primitive prop
In Vue 3, defineProps() is shallow readonly. Mutating it causes a dev warning and is considered an anti-pattern.
<!-- ChildCounter.vue (BAD) -->
<script setup>
const props = defineProps({
count: { type: Number, required: true }
});
function inc() {
// ❌ anti-pattern: mutating a prop
// Vue warns in dev; parent can overwrite on next render.
props.count++;
}
</script>
<template>
<button @click="inc">Count: {{ count }}</button>
</template>
Correct pattern: emit an update (parent stays the source of truth)
The child requests a change; the parent applies it.
<!-- ChildCounter.vue (GOOD) -->
<script setup>
const props = defineProps({
count: { type: Number, required: true }
});
const emit = defineEmits(['update:count']);
function inc() {
emit('update:count', props.count + 1);
}
</script>
<template>
<button @click="inc">Count: {{ count }}</button>
</template>
<!-- Parent.vue -->
<script setup>
import { ref } from 'vue';
import ChildCounter from './ChildCounter.vue';
const count = ref(0);
</script>
<template>
<!-- v-model:count = :count + @update:count -->
<ChildCounter v-model:count="count" />
</template>
The tricky case: object/array props
Even if Vue warns, mutating props.user.name is still mutating the same object the parent passed. This couples child behavior to parent state and makes bugs look “random” because the mutation didn’t go through an explicit parent update.
<!-- ChildUserEditor.vue (BAD) -->
<script setup>
const props = defineProps({
user: { type: Object, required: true }
});
function rename() {
// ❌ Mutates parent-owned object via shared reference
props.user.name = 'New Name';
}
</script>
<template>
<button @click="rename">Rename</button>
</template>
When you need “editable local state”
Make a local copy for editing, and emit the final value (or emit on each change).
<!-- ChildNameInput.vue (local draft -> emit) -->
<script setup>
import { ref, watch } from 'vue';
const props = defineProps({
name: { type: String, required: true }
});
const emit = defineEmits(['update:name']);
const draft = ref(props.name);
// Keep draft in sync if parent changes the prop externally
watch(() => props.name, (v) => { draft.value = v; });
function commit() {
emit('update:name', draft.value);
}
</script>
<template>
<input v-model="draft" />
<button @click="commit">Save</button>
</template>
Interview-ready takeaway
Mutating props breaks one-way data flow: the parent is no longer the single source of truth, updates become non-deterministic to trace, and nested object/array props can mutate parent state by reference. Correct approach: child emits intent (update events / v-model) and the parent owns the actual state mutation.
Use this as one explanation rep, then continue with the Vue.js interview questions cluster or a guided prep path.