skip to content

Runtime Built-ins

The components Vue ships without an import in templates: Transition, TransitionGroup, KeepAlive, Teleport and Suspense. Interviewers probe what each changes about mounting and timing.

part ofVue.jsoverview, primer and where to startread it →
on this pageshow

explore

questions

23

In Vue 3, what changes when you wrap a tab's dynamic component in `<KeepAlive>` and the user leaves a half-filled long form and comes back?

level: juniorimportance: must knowfreq 58%

answer

  1. unmount versus set aside
  2. same instance comes back
  3. DOM parked off-document
  4. setup and onMounted not re-run

basics

~20 s

Without KeepAlive, Vue unmounts the form's component when the tab switches away and builds a fresh one on return, losing every typed value. KeepAlive deactivates and caches the instance instead, so the same instance, state and DOM come back.

solid answer

~40 s

Switching a `<component :is>` normally unmounts the old component: its instance, refs and DOM are destroyed, and coming back runs `setup()` again with initial state, so a half-filled form is empty. Wrapping it in `<KeepAlive>` changes what happens on switch-away: Vue **deactivates** the instance, moves its DOM into a detached container and keeps it in a cache. On return the same instance is **activated** and its DOM is moved back, so reactive state, input values and child components are exactly as the user left them. `setup()` and `onMounted` do not run again; the cached instance gets `onActivated`/`onDeactivated` instead. The price is memory for every cached instance, which `include`, `exclude` and `max` bound.

code

vue · 11 lines
vue
<script setup lang="ts">
import { ref, onMounted, onActivated } from 'vue'

const email = ref('')
onMounted(() => console.log('mounted once'))
onActivated(() => console.log('shown: first mount and every return'))
</script>

<template>
  <input v-model="email" placeholder="Email" />
</template>

go deeper

for a junior

Remember that KeepAlive keeps a switched-out component's instance and state instead of destroying it, so a form's values survive switching tabs.

for a middle

Explain deactivate versus unmount: the DOM moves to a detached container, setup and onMounted do not re-run, and onActivated/onDeactivated fire instead.

for a senior

Point out that cached instances stay live, so background work continues and memory grows; bound the cache and pause work on deactivation.

for a principal

Decide when view-local caching is the right home for state versus lifting it into a store or the URL, weighing memory, freshness and reload survival.

## The default: switching destroys A tabbed settings page often renders the active tab with a **dynamic component**, `<component :is="currentTab" />`. When `currentTab` changes, Vue treats the old component like any other element that is no longer rendered: it **unmounts** it. Unmounting destroys the component instance, its reactive state, its child components and its DOM. When the user returns, Vue creates a brand-new instance, runs `setup()` again and renders initial state. For a long form that means every field the user filled in is gone. The same is true for scroll positions inside the tab, expanded accordions and anything else held in the instance. ## What `<KeepAlive>` changes `<KeepAlive>` is a built-in component from the `vue` package. It wraps a single component child and **caches component instances instead of destroying them**: ```vue <script setup lang="ts"> import { shallowRef } from 'vue' import ProfileForm from './ProfileForm.vue' import BillingForm from './BillingForm.vue' const currentTab = shallowRef(ProfileForm) </script> <template> <button @click="currentTab = ProfileForm">Profile</button> <button @click="currentTab = BillingForm">Billing</button> <KeepAlive> <component :is="currentTab" /> </KeepAlive> </template> ``` Now the life of each tab looks different: 1. **First visit**: the component mounts normally, and because it is under `<KeepAlive>`, `onActivated` also runs right after `onMounted`. 2. **Switch away**: instead of unmounting, Vue **deactivates** the instance. Its DOM is moved into an off-document container that `<KeepAlive>` owns, and `onDeactivated` runs. 3. **Switch back**: Vue finds the instance in the cache, moves its DOM back into the page and runs `onActivated`. No new instance is created, so `setup()` and `onMounted` do not run. The typed-in values survive because the very same input elements and the very same refs are put back. ## Unmounted versus deactivated | | Without `<KeepAlive>` | With `<KeepAlive>` | |---|---|---| | Instance on switch-away | Destroyed | Kept in the cache | | DOM on switch-away | Removed and discarded | Moved to a detached container | | Hooks on switch-away | `onBeforeUnmount`, `onUnmounted` | `onDeactivated` | | Hooks on return | Full `setup()` and mount hooks | `onActivated` only | | State on return | Initial values | Exactly as left | | Memory while hidden | Freed | Held | ## What "cached" really means - The instance is **alive**, not frozen. Its reactive effects and watchers are not stopped, so timers, polling or event listeners it registered keep running while it is hidden unless you pause them in `onDeactivated`. - `onActivated` and `onDeactivated` fire for the cached component **and for its descendants**, so a child inside the form can react to being shown again too. - Only one component child may be active inside `<KeepAlive>` at a time; with several children Vue warns `KeepAlive should contain exactly one component child.` - A plain element child is rendered but never cached; caching works on component instances. ## Bounding the cache By default every component that passes through `<KeepAlive>` is cached. Three props control that: - `include`: only components whose **name** matches are cached; - `exclude`: components whose name matches are never cached; - `max`: the maximum number of cached instances; beyond it the least recently accessed one is destroyed. ## Common interview traps Interviewers often probe whether a candidate knows what KeepAlive is **not**: - It is not `v-show`. `v-show` leaves the element in place and toggles `display`; KeepAlive takes the DOM out of the page entirely and puts it back later. - It is not a persistence layer. Nothing is written to storage, and a page reload loses every cached instance. - It does not snapshot and restore values. The state survives because the **same objects** are reused, which is also why they can be stale. - It is not free. A cached instance keeps its memory and its running effects until it is evicted. Knowing these four distinctions usually matters more in an interview than reciting the prop list. ## When not to reach for it KeepAlive trades memory and freshness for preserved state. If the form's data must survive a page reload, or be shared with other views, the state belongs outside the component, and the component can stay disposable. KeepAlive is the right tool when the state is purely view-local and the cost of rebuilding it, including what the user typed, is what you want to avoid. ## Summary `<KeepAlive>` turns "switch away" from **unmount** into **deactivate**. The instance, its state and its DOM are parked and later reinserted, which is why the half-filled form comes back intact and why the lifecycle you see on return is `onActivated`, not `setup()` and `onMounted`.

  • Where do the cached component's DOM nodes live while it is hidden?
    `<KeepAlive>` owns a detached container element created off the document. On deactivation Vue moves the component's DOM into it, and on activation moves it back into the page. Nothing is re-created, which is why input values, scroll positions inside the component and child state survive.
  • Does a hidden, cached component stop reacting to state changes?
    No. Deactivation does not stop the instance's reactive effects, so its watchers and any intervals or listeners it set up keep running. That is usually what surprises people: background polling continues for a tab nobody is looking at. Pause such work in `onDeactivated` and resume it in `onActivated`.

A coat check: you hand over your coat with everything still in its pockets and get the very same coat back, rather than being issued a new one.

saying these in an interview costs you the question

  • KeepAlive re-runs setup() on return but restores the old values
  • KeepAlive hides the component with display: none, like v-show
  • Cached components are frozen, so their watchers stop while hidden
  • onMounted runs every time a cached tab is shown again
  • KeepAlive can cache several active children at once
open as a page

In Vue 3, how does the built-in `<Teleport>` fix a modal clipped inside a deeply nested scrolling container, and what does its `to` prop accept?

level: juniorimportance: must knowfreq 62%

basics

~20 s

Vue's <Teleport> renders its slot content into another DOM node, typically body, so ancestor overflow, transforms and stacking contexts stop constraining the modal. Its to prop takes a CSS selector string or an actual DOM element.

open as a page

In Vue 3, which six CSS classes does `<Transition>` apply to an entering and a leaving element, and when is each one added and removed?

level: juniorimportance: must knowfreq 66%

basics

~20 s

Vue 3's Transition adds v-enter-from, v-enter-active and v-enter-to on insert, and v-leave-from, v-leave-active and v-leave-to on removal. The from class is swapped for the to class a frame later; the active class lasts the whole phase.

open as a page

In a Vue 3 component cached by `<KeepAlive>`, when exactly do `onActivated` and `onDeactivated` run, and what work belongs in them?

level: middleimportance: must knowfreq 50%

basics

~20 s

In Vue 3, onActivated runs on the cached component's first mount and every time it is reinserted; onDeactivated runs each time it is moved into the cache and on final unmount. Use them for show/hide work: refresh, pause, resume.

open as a page

In Vue 3, when a child component renders inside `<Teleport to="body">`, which relationships follow the component tree and which follow the DOM?

level: middleimportance: must knowfreq 52%

basics

~20 s

Vue relationships follow the component tree: props, emitted events, injections, Devtools nesting and reactivity work as without Teleport. Anything the browser computes from DOM position follows the target: native event bubbling, ancestor-based CSS selectors and contains() checks.

open as a page

What does Vue 3's built-in `<Suspense>` component do, and what belongs in its #default and #fallback slots?

level: juniorimportance: should knowfreq 45%

basics

~20 s

<Suspense> renders its #default content in memory and, while async dependencies inside it are unresolved, shows the #fallback slot instead; once all of them resolve it swaps the finished content in. It is still an experimental Vue feature.

open as a page

In Vue 3, how does `<TransitionGroup>` differ from `<Transition>`, and why does it require keys but offer no `mode` prop?

level: juniorimportance: should knowfreq 46%

basics

~20 s

Vue's TransitionGroup animates many list items at once: each item gets the enter/leave classes, moved items get a move class, and no wrapper renders unless tag is set. Keys identify which items entered, left or moved; mode is absent because nothing alternates.

open as a page

In Vue 3, why can `<KeepAlive include="CheckoutForm">` fail to cache a component, and how does Vue match `include` and `exclude`?

level: middleimportance: should knowfreq 44%

basics

~20 s

Vue's KeepAlive matches include and exclude against the component's name option, not its import or tag. A component with no matching name, a misspelled filename-inferred name, or a spaced comma list is simply not cached.

open as a page

In a Vue 3 dashboard whose widgets use top-level await, when does a `<Suspense>` around it show the widgets, and what delays them?

level: middleimportance: should knowfreq 45%

basics

~20 s

The widgets appear together once every async dependency Suspense found has resolved, so the slowest chain of dependencies decides. Sibling widgets' awaits run in parallel, but a widget nested inside another async component only starts after its parent resolves, creating a waterfall.

open as a page

In Vue 3, how does `<Teleport>`'s `disabled` prop render content inline on mobile but in body on desktop, and does state survive toggling?

level: middleimportance: should knowfreq 40%

basics

~10 s

Binding :disabled="isMobile" on <Teleport> renders the content in place while true and in the target while false. Toggling moves the existing DOM nodes, so component state, input values and lifecycle are preserved; nothing remounts.

open as a page

In Vue 3, when a `<TransitionGroup>` list is shuffled, how does the move class slide items to their new positions, and what CSS does it need?

level: middleimportance: should knowfreq 38%

basics

~20 s

Vue's TransitionGroup records each item's position before the update, measures again after, translates moved items back to their old spot, then adds the move class and clears the transform so a CSS transform transition slides them: the FLIP technique.

open as a page

In Vue 3, how do you animate a `<Transition>` with JavaScript hooks, and what do the `done` callback and `:css="false"` change?

level: middleimportance: should knowfreq 38%

basics

~20 s

Vue 3's Transition emits before-enter, enter, after-enter, before-leave, leave and after-leave. Enter and leave receive the element and a done callback; setting css to false skips classes and CSS detection, so calling done is what ends the phase.

open as a page

In Vue 3, when `<Transition>` swaps one view for another, what do `mode="out-in"` and `mode="in-out"` change, and what happens with no mode?

level: middleimportance: should knowfreq 52%

basics

~20 s

With no mode, Vue 3's Transition runs the old element's leave and the new element's enter together. Mode out-in finishes the leave before inserting the new element; in-out enters the new element first and only then starts the old one's leave.

open as a page

A Vue 3 order-details tab wrapped in `<KeepAlive>` shows outdated data after switching back and keeps polling while hidden; why, and how do you fix it?

level: seniorimportance: should knowfreq 38%

basics

~20 s

Vue's KeepAlive reuses the instance, so data loaded in setup or onMounted is never refetched, and its timers and watchers keep running while hidden. Refetch in onActivated, pause in onDeactivated, and key or evict entries whose identity changed.

open as a page

When combining Vue 3's `<Transition>`, `<KeepAlive>` and `<Suspense>` around a dynamic component, in what order do you nest them, and why?

level: seniorimportance: should knowfreq 30%

basics

~20 s

Nest them as Transition, then KeepAlive, then Suspense, with the dynamic component directly inside Suspense. That makes each component switch replace Suspense's root, so it can wait on the new view, while KeepAlive caches and Transition animates the actual component.

open as a page

A Vue 3 app warns "Failed to locate Teleport target with selector" for a container another component renders; why does this happen, and how does `defer` fix it?

level: seniorimportance: should knowfreq 34%

basics

~20 s

A Teleport resolves its to selector when it mounts, and a container Vue renders in the same pass is not yet in the document. Vue 3.5's defer delays the lookup until the rest of that mount or update tick has mounted.

open as a page

A Vue 3 `<TransitionGroup>` list animates wrongly: removing a middle row fades out the last row, and shuffles jump instead of sliding; how do you diagnose it?

level: seniorimportance: should knowfreq 30%

basics

~20 s

In Vue's TransitionGroup, the last row fading on a middle removal means index keys: Vue sees the last key disappear. Jumpy shuffles mean no detectable moves: index keys, no transform transition on the move class, inline elements, or leaving rows still in flow.

open as a page

A Vue 3 toast component has `<Transition>` as its template root and the parent renders it with `v-if`; why does neither the fade-in nor the fade-out play, and how do you fix it?

level: seniorimportance: should knowfreq 40%

basics

~20 s

Vue 3's Transition animates its slot content changing, not its own mount or unmount. The parent's v-if creates and destroys the Transition with the toast, so enter is an unanimated first render and leave is skipped.

open as a page

In Vue 3, how does `<KeepAlive :max>` decide which cached instance to destroy, and what happens to the evicted instance?

level: middleimportance: nice to knowfreq 30%

basics

~20 s

Vue's KeepAlive max behaves like an LRU cache: when adding a new entry would exceed max, the least recently accessed cached instance is destroyed. Revisiting an entry makes it most recent. The evicted instance is fully unmounted and its state is lost.

open as a page

After a Vue 3 `<Suspense>` has resolved, what makes it pending again, and what do its timeout prop and pending, resolve and fallback events control?

level: middleimportance: nice to knowfreq 30%

basics

~20 s

A resolved <Suspense> goes pending again only when the root node of its #default slot is replaced. It then keeps showing the old content until the new content resolves, unless timeout sets when to switch to #fallback; pending, fallback and resolve events report each transition.

open as a page

In Vue 3, how does `<Transition>` decide when a CSS enter or leave has finished, and when do you need its `type` or `duration` props?

level: middleimportance: nice to knowfreq 24%

basics

~20 s

Vue 3's Transition reads the element's computed transition and animation delays and durations, then waits for matching end events from that element, with a fallback timer. Set type when both are used; set duration when nested children animate longer.

open as a page

In Vue 3, how do you stagger `<TransitionGroup>` item animations so each list item enters slightly after the previous one?

level: middleimportance: nice to knowfreq 22%

basics

~10 s

Vue's TransitionGroup staggers by giving each item its index as a data attribute, then using JavaScript hooks with :css="false" that read el.dataset.index to delay each item's animation and call done when it finishes.

open as a page

Vue 3's `<Suspense>` is still labelled experimental: what does that mean in practice, and how would you use it safely in a production app?

level: seniorimportance: nice to knowfreq 25%

basics

~20 s

Experimental means the Vue team does not guarantee the API will stay as it is or reach stable status; it works today and keeps receiving fixes. Use it behind a small wrapper component, handle errors in an ancestor, and track release notes.

open as a page