skip to content

In Vue 3, in what order do setup, onBeforeMount and onMounted run for a parent and its child, and how does unmounting order them?

level: middleimportance: must knowfreq 60%

answer

  1. setup runs top-down
  2. mounted is inside-out
  3. the parent's patch mounts the child
  4. beforeUnmount top-down, unmounted bottom-up

basics

~10 s

Mounting goes parent setup, parent onBeforeMount, child setup, child onBeforeMount, child onMounted, then parent onMounted. Unmounting goes parent onBeforeUnmount, child onBeforeUnmount, child onUnmounted, then parent onUnmounted.

solid answer

~40 s

The parent sets up and calls `onBeforeMount` first, then renders; creating its child happens during that render and patch, so the child's setup and `onBeforeMount` run next. The child finishes first, so its `onMounted` runs before the parent's: Vue only considers the parent mounted once all its synchronous children are mounted. Siblings follow template order. Teardown mirrors it: the parent's `onBeforeUnmount` runs first while everything is intact, then each child's `onBeforeUnmount`, then the child's `onUnmounted`, and the parent's `onUnmounted` comes last, after all its children are gone. This is why a parent's `onMounted` can safely read its children's DOM, and a child's cleanup never finds its parent already unmounted.

code

ts · 25 lines
ts
import { defineComponent, h, onBeforeMount, onMounted, onBeforeUnmount, onUnmounted } from 'vue'

const log = (s: string) => console.log(s)

const Child = defineComponent(() => {
  log('child setup')
  onBeforeMount(() => log('child beforeMount'))
  onMounted(() => log('child mounted'))
  onBeforeUnmount(() => log('child beforeUnmount'))
  onUnmounted(() => log('child unmounted'))
  return () => h('span', 'child')
})

export const Parent = defineComponent(() => {
  log('parent setup')
  onBeforeMount(() => log('parent beforeMount'))
  onMounted(() => log('parent mounted'))
  onBeforeUnmount(() => log('parent beforeUnmount'))
  onUnmounted(() => log('parent unmounted'))
  return () => h('div', [h(Child)])
})

// mount: parent setup, parent beforeMount, child setup, child beforeMount,
//        child mounted, parent mounted
// unmount: parent beforeUnmount, child beforeUnmount, child unmounted, parent unmounted

go deeper

for a junior

Recall the two sequences: setup and beforeMount go parent first, mounted goes child first; unmounted also goes child first.

for a middle

Explain why: the child is created inside the parent's render and patch, and mounted hooks are queued as each component finishes.

for a senior

Name the exceptions, async components, Suspense content, v-if children and detached root containers, and design measurement and cleanup code that does not rely on the naive order.

for a principal

Use the order to decide ownership: shared resources belong to the ancestor that outlives its consumers, released in its onUnmounted after every child has let go.

## Why the order matters Knowing the exact sequence answers practical questions: can the parent measure its children in `onMounted`? Can a child call something the parent provided during its own `onUnmounted`? Does a parent's `onBeforeMount` see its children? Vue 3 fixes the order, so the answers are deterministic. ## Mounting: top-down setup, inside-out mounted For a `Parent` whose template contains one `Child`: 1. `Parent` setup (the `<script setup>` body) 2. `Parent` `onBeforeMount` 3. `Child` setup 4. `Child` `onBeforeMount` 5. `Child` `onMounted` 6. `Parent` `onMounted` The reason is structural. The parent's render effect calls `onBeforeMount`, renders the parent's template and patches it into DOM. Patching the child's vnode creates the child instance, so the child's setup, `onBeforeMount` and render all happen **inside** the parent's patch. `onMounted` callbacks are queued as post-flush callbacks as each component finishes, so the child's callback is queued first and runs first. The Vue API reference states the rule from the other side: a component is considered mounted after **all of its synchronous child components have been mounted** and its own DOM tree has been inserted into the parent container. With several children, `A` and `B` in template order, setup runs `Parent`, `A`, `B` (with each child's own `onBeforeMount` and descendants right after its setup), and `onMounted` runs `A`, `B`, then `Parent`. ## Unmounting: beforeUnmount top-down, unmounted bottom-up When the parent is removed: 1. `Parent` `onBeforeUnmount` 2. `Child` `onBeforeUnmount` 3. `Child` `onUnmounted` 4. `Parent` `onUnmounted` Vue calls the parent's `onBeforeUnmount` first, while the whole subtree is intact, then stops the parent's effects and unmounts its subtree, which runs each child's `onBeforeUnmount` and queues the child's `onUnmounted`. The parent's `onUnmounted` is queued after that, so it runs last. The reference again states it: a component is considered unmounted after **all of its child components have been unmounted**. ## The order at a glance | Step | Mount | Unmount | |---|---|---| | First | parent setup, parent `onBeforeMount` | parent `onBeforeUnmount` | | Middle | child setup, child `onBeforeMount` | child `onBeforeUnmount` | | Then | child `onMounted` | child `onUnmounted` | | Last | parent `onMounted` | parent `onUnmounted` | ## Practical consequences - **A parent can read its children's DOM in `onMounted`**, because the synchronous children are already mounted. - **A parent's `onBeforeMount` sees no children**: none exist yet. - **A child's cleanup runs before the parent's**, so a parent tearing down a shared resource in `onUnmounted` does so after every child has released its use of it. - **The Options API follows the same order** with `beforeCreate`/`created` inside the setup step, then `beforeMount`, `mounted`, `beforeUnmount`, `unmounted`. ## A bug the order explains A `MapView` parent creates a third-party map object in its `onMounted`, because the library needs the container element. Each `MapMarker` child adds itself to the map in its own `onMounted`. The markers never appear, or the console reports that the map is undefined. The cause is the inside-out order: every child's `onMounted` has already run by the time the parent's `onMounted` creates the map. Fixes that respect the order: - Provide a `shallowRef` for the map from the parent's setup and have each marker `watch` it, adding itself once it becomes non-null. - Or render the markers only after the map exists, for example behind a `v-if` on a `mapReady` flag set in the parent's `onMounted`, so the markers mount in a later update. ## Exceptions to keep in mind - **Async components and `<Suspense>` content** are not part of "all synchronous children": a parent's `onMounted` can run before they are mounted. - **Children added later**, for example by a `v-if` becoming true, mount during a later update of the parent; their hooks run then, and the parent's `onMounted` does not run again. - **Being mounted is not being visible**: the reference notes the DOM is only guaranteed to be in the document if the application's root container is. An interview answer that gives the sequence, the reason (children are created inside the parent's render and patch), and at least one exception shows real understanding rather than memorisation.

  • Can a Vue 3 parent measure its children's elements inside its own onMounted?
    Yes, for synchronous children: Vue considers a component mounted only after all its synchronous children are mounted, so their DOM already exists when the parent's `onMounted` runs. It does not hold for async components or content inside `<Suspense>`, which can mount later.
  • If a child is added later with v-if, does the parent's onMounted run again?
    No. `onMounted` runs once per instance. A child created by a later `v-if` flip is mounted during the parent's update; the child's own hooks run then, and the parent sees an update, not a second mount. Code that must react to that child should live in the child or respond to the condition.

Building a house: the frame goes up before the rooms are fitted, but the house is only handed over once every room is finished. Demolition starts with announcing it to the whole house, then rooms are cleared before the frame comes down.

saying these in an interview costs you the question

  • The parent's onMounted runs before its children's
  • Child setup runs before the parent's setup
  • A parent's onBeforeMount can already see its children
  • The parent's onUnmounted runs before its children's
  • onMounted waits for async child components too