KeepAlive, Teleport, and Suspense
|
This section documents the current Vue 3.x release line as published at
the official Vue.js documentation, which is the reference these pages are written and
verified against. No specific patch version is pinned. The Composition API with This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production, since Vue and its ecosystem iterate quickly. This section’s bibliography lists the reference material consulted while preparing these pages. |
<KeepAlive>, <Teleport>, and <Suspense> are built-in components that change where and how a subtree
lives: kept in memory instead of destroyed, rendered into a different part of the DOM, or held back behind a
fallback until its async dependencies resolve.
<KeepAlive>
A component toggled out by v-if or replaced through :is is normally unmounted, losing its state. Wrap it
in <KeepAlive> to cache the instance instead:
<script setup>
import { shallowRef } from 'vue'
import Home from './Home.vue'
import Settings from './Settings.vue'
const tabs = { Home, Settings }
const current = shallowRef('Home')
</script>
<template>
<button v-for="(_, name) in tabs" :key="name" @click="current = name">
{{ name }}
</button>
<KeepAlive>
<component :is="tabs[current]" />
</KeepAlive>
</template>
Switching tabs now preserves scroll position, form input, and any other in-component state. See
<KeepAlive>.
include, exclude, max
By default every child is cached. Restrict the set by component name (a string, comma-separated string,
RegExp, or array of those), and bound the pool with max, which turns it into an LRU cache:
<KeepAlive :include="['Home', 'Settings']" :exclude="/Modal$/" :max="10">
<component :is="view" />
</KeepAlive>
Matching is against each component’s name option, or the name inferred from its filename in
<script setup> (Vue 3.3+).
onActivated / onDeactivated
A cached component is not unmounted, so onMounted and onUnmounted do not fire when it is toggled. Use the
KeepAlive-specific hooks instead:
<script setup>
import { onActivated, onDeactivated } from 'vue'
onActivated(() => {
// re-entered from the cache -- resume polling, refetch, restore focus
})
onDeactivated(() => {
// cached away -- pause timers and subscriptions
})
</script>
onActivated also runs on the initial mount and onDeactivated on the final unmount. Both hooks fire for
the component and for any descendant that is also inside the cached tree. See
onActivated.
<Teleport>
Renders its slot content at a different location in the DOM while keeping it a logical child of the current
component — props, provide/inject, and emitted events all still work. The classic case is a modal that must
escape an ancestor’s overflow: hidden or z-index stacking context:
<script setup>
import { ref } from 'vue'
const open = ref(false)
</script>
<template>
<button @click="open = true">open</button>
<Teleport to="body">
<div v-if="open" class="backdrop" @click="open = false">
<div class="modal">I render at the end of <body></div>
</div>
</Teleport>
</template>
to accepts a CSS selector string or an actual DOM element. See
<Teleport>.
disabled
Toggle teleporting at runtime — for example, keep a video player inline on desktop but move it to a full-screen container on mobile:
<Teleport to="#fullscreen" :disabled="!isMobile">
<VideoPlayer :src="src" />
</Teleport>
Deferred teleport
If the target element is rendered by Vue later in the same tree, it does not exist yet when <Teleport>
mounts. Since Vue 3.5, defer postpones resolving the target until after the current render tick:
<template>
<Teleport defer to="#late-panel">
<p>waits one tick for the target to exist</p>
</Teleport>
<!-- rendered by Vue, after the Teleport above -->
<div id="late-panel"></div>
</template>
Multiple teleports to one target
Several <Teleport> components aimed at the same target append their content in mount order — this is how a
toast or notification stack is built:
<template>
<Teleport to="#toasts"><Toast>Saved</Toast></Teleport>
<Teleport to="#toasts"><Toast>Uploaded</Toast></Teleport>
</template>
<Suspense>
<Suspense> is an experimental feature — its API may still change.
|
<Suspense> shows a fallback until every async dependency in its #default slot has resolved. Async
dependencies are async components and components whose <script setup>
contains a top-level await:
<template>
<Suspense>
<template #default>
<UserDashboard />
</template>
<template #fallback>
<p>Loading dashboard…</p>
</template>
</Suspense>
</template>
<script setup>
// UserDashboard.vue -- top-level await makes this an async dependency
const res = await fetch('/api/me')
const user = await res.json()
</script>
<template>
<h1>{{ user.name }}</h1>
</template>
See <Suspense>.
Events and state flow
<Suspense> emits an event at each stage:
| Event | Fires when |
|---|---|
|
a new async branch begins — initial load, or a keyed re-render |
|
every dependency in |
|
the fallback slot is displayed — on initial load, or when |
<Suspense> also accepts timeout (milliseconds to wait before showing the fallback on an update) and
suspensible (defer handling to a parent <Suspense>). Pair it with a
defineAsyncComponent boundary, and handle the rejected case with an
error boundary using onErrorCaptured.
Special elements and attributes
These read like components or plain attributes but are handled by the compiler. They are listed in Built-in Special Elements and Special Attributes.
| Name | Purpose |
|---|---|
|
render a component chosen at runtime — a registered name, an imported component
object, or a plain HTML tag string. Combine with |
|
outlet for parent-supplied content; |
|
a wrapper that renders nothing — used to host |
|
a hint to replace rather than patch a vnode — essential on |
|
obtain an element or component instance — a string name in the template, read via
|
|
on a native tag, |
<script setup>
import { useTemplateRef, onMounted } from 'vue'
const field = useTemplateRef('field')
onMounted(() => field.value.focus())
</script>
<template>
<input ref="field" />
</template>
See also
-
Async Components —
defineAsyncComponent, loading and error states, and how it pairs with<Suspense>. -
Transitions and Animation — animating
<component :is>swaps and teleported modals. -
Routing —
<RouterView>with<KeepAlive>and<Transition>for cached, animated route views.