skip to content

A Vue 3 parent renders three child components and changes a prop passed to only one of them; which components re-render, and why?

level: middleimportance: must knowfreq 62%

answer

  1. one render effect per component
  2. parent read the changed state
  3. props compared when patching a child
  4. siblings skipped, not re-rendered

basics

~20 s

The parent re-renders because its render read the changed state. The child whose prop changed re-renders. The two siblings are reached by the patch but skipped, because their props compare equal, so their render functions and descendants do not run.

solid answer

~40 s

Every Vue 3 component instance has its **own render effect**. The parent's template read the value it passes down, so changing it re-runs the **parent's** render and produces new child vnodes. While patching each child component vnode, the renderer calls `shouldUpdateComponent`: with compiled templates it compares only the props the compiler marked dynamic, value by value with `!==`, and also forces an update for dynamic slot content or a directive or transition on the component vnode. The changed child gets its render run; the unchanged siblings just receive the new vnode and keep their DOM. The same check repeats one level down, so a grandchild re-renders only if its props changed too. If the state were read only inside the child, only the child would re-render — the parent never tracked it.

go deeper

for a junior

Recall that each Vue component re-renders on its own, and that a parent's update does not automatically re-render every child.

for a middle

Walk through the per-component render effect and the props comparison at each child boundary, including which cases force a child update.

for a senior

Use this model to explain unexpected updates in production, such as inline object props or dynamic slots causing children to re-render every time.

for a principal

Treat component boundaries as the update granularity when reviewing architecture: where state lives decides which subtrees pay for a change.

## The setup Assume a `<script setup>` parent that owns `count` and renders three children: ```vue <script setup lang="ts"> import { ref } from 'vue' import Stat from './Stat.vue' const count = ref(0) const total = ref(10) </script> <template> <Stat label="Count" :value="count" /> <Stat label="Total" :value="total" /> <Stat label="Static" value="n/a" /> <button @click="count++">add</button> </template> ``` Clicking the button changes only `count`. The question is which render functions run. ## One render effect per component In Vue 3 each component instance gets exactly one **render effect** — a reactive effect wrapping the function that renders it and patches the result. The effect remembers which reactive values were read during the last render. Two consequences: - A component re-renders when **something it read** changes, regardless of where that state lives. - A component does **not** re-render merely because its parent did; the parent has to decide, per child, that something passed in has changed. The unit of update is therefore the **component**, not the whole subtree below the component that owns the state, and not a single DOM node. ## Walking the example 1. `count` changes. The parent's template read `count` (to pass it as `:value`), so the **parent's** render effect is scheduled and re-runs, returning a new vnode tree with three new `Stat` vnodes. 2. The renderer patches the parent's old tree against the new one and reaches the first `Stat`. Old and new are the same component type, so it calls `updateComponent`, which asks `shouldUpdateComponent(old, new)`. 3. For the first `Stat`, the compiler flagged `value` as a dynamic prop; `1 !== 0`, so the answer is yes. The renderer sets the instance's pending vnode and runs that child's render effect: its props are updated and its template renders. 4. For the second `Stat`, `value` is dynamic but `10 === 10`, so the answer is no. The renderer copies `el` to the new vnode and moves on — `Stat`'s render function does not run. 5. The third `Stat` has only static props, so there is nothing to compare and it is skipped the same way. Result: **the parent and the first child** re-render. Anything below the second and third children is untouched. Below the first child, the same props check decides each grandchild independently. ## What the child check looks at | Situation on the child vnode | Child re-renders? | |---|---| | A dynamic prop's value differs (`!==`) | Yes | | All props equal, no other flags | No | | A listener declared in the child's `emits` changed | No — declared emit listeners are ignored in the comparison | | Slot content depends on `v-if`/`v-for` in the parent (dynamic slots) | Yes | | A runtime directive or a `<Transition>` applies to the component vnode | Yes | | Hand-written render function passing children that are not marked stable | Yes | Note that the comparison is **shallow**: an object prop compares by reference. Replacing an array with an equal copy counts as a change; mutating an object in place does not. ## When the state lives somewhere else Move the value out of the parent — say, into a shared `reactive()` object that only the child reads — and the picture changes. The parent's render never read it, so the parent does not re-render at all; only the child's render effect re-runs. The same applies to an object prop mutated in place: `user.name = 'x'` re-renders the components whose renders read `user.name`, and the parent only if its own template read it too. Granularity follows **reads**, not the shape of the component tree. ## Why interviewers ask this The question separates candidates who carry a React mental model ("a parent re-render re-renders its children") from those who know Vue's: tracked reads per component plus a props check at each child boundary. It also sets up the practical advice owned elsewhere — keeping props stable so the check can say no. - Know that the parent re-renders only because **it** read the state. - Know that siblings are **visited** by the patch but **not re-rendered**. - Know the exceptions that force a child update even with equal props. - Know that a new object or array literal written inline in the parent template is a new reference on every parent render, so the child it is passed to fails the check each time. - Know that the check runs per child: one noisy child does not drag its siblings along. A strong answer names the unit of work — one render effect per component instance — before describing any optimization.

  • In Vue 3, if the parent mutates `user.name` on a reactive object passed as the `user` prop, which components re-render?
    The prop reference is unchanged, so the parent-side props check sees nothing new. Components re-render only if their own render read `user.name`: the child that displays it re-renders through its own effect, and the parent re-renders only if its template read `user.name` as well.
  • In Vue 3, when does a child re-render even though none of its props changed?
    When reactive state it read during its own render changes; when the slot content passed to it is dynamic (built under `v-if` or `v-for`); when a runtime directive or a transition is applied to its component vnode; or when a hand-written render function passes children that are not marked stable.
  • How does this compare with React's default behaviour?
    In React, re-rendering a parent calls every child component it renders again unless the child is wrapped in `memo`, which compares props shallowly. Vue performs a props comparison at every child boundary by default, and additionally re-renders any component directly from its own tracked reads, without involving the parent.

saying these in an interview costs you the question

  • Says all three children re-render because their parent re-rendered.
  • Believes only the changed child re-renders and the parent never does.
  • Thinks Vue deep-compares object props before deciding to update a child.
  • Assumes a child re-renders when an unrelated sibling's prop changes.
  • Claims mutating a nested field of an object prop re-renders the parent regardless.