Skip to content
← All questions
⚡ Intermediate

What are dynamic components and how does KeepAlive work?

🧩 Components⚡ Performance🎰 Slots⏳ Suspense🌀 Teleport

Dynamic components let you switch between components at runtime using the built-in <component> element with the :is prop. By default, Vue destroys the old component and creates a new one on each switch. <KeepAlive> changes that behavior by caching instances in memory.

Dynamic components ​

vue
<script setup>
import { ref, shallowRef } from 'vue'
import TabHome from './TabHome.vue'
import TabSettings from './TabSettings.vue'
import TabProfile from './TabProfile.vue'

const tabs = { TabHome, TabSettings, TabProfile }
const currentTab = shallowRef(TabHome)
</script>

<template>
  <button v-for="(comp, name) in tabs" :key="name" @click="currentTab = comp">
    {{ name }}
  </button>

  <!-- Destroyed and recreated on every switch -->
  <component :is="currentTab" />
</template>
Open in Vue Playground

Without KeepAlive, any local state in TabSettings (form inputs, scroll position, expanded sections) resets every time you switch away and back.

Comparison diagram: without KeepAlive components are destroyed on switch, with KeepAlive they are cached

Adding KeepAlive ​

Wrap <component> in <KeepAlive> to cache instances instead of destroying them.

vue
<template>
  <KeepAlive>
    <component :is="currentTab" />
  </KeepAlive>
</template>
Open in Vue Playground

Now switching tabs preserves each component's full state.

Controlling what gets cached ​

Use include, exclude, and max to limit caching.

vue
<template>
  <!-- Only cache these two -->
  <KeepAlive include="TabHome,TabSettings">
    <component :is="currentTab" />
  </KeepAlive>

  <!-- Cache everything except this one -->
  <KeepAlive exclude="TabProfile">
    <component :is="currentTab" />
  </KeepAlive>

  <!-- Cache at most 5 instances (LRU eviction) -->
  <KeepAlive :max="5">
    <component :is="currentTab" />
  </KeepAlive>
</template>
Open in Vue Playground

include and exclude match the component's name. Set it explicitly with defineOptions:

vue
<script setup>
defineOptions({ name: 'TabSettings' })
</script>
Open in Vue Playground

Lifecycle hooks for cached components ​

Cached components don't fire onMounted/onUnmounted on switch. Use onActivated and onDeactivated instead.

vue
<script setup>
import { onActivated, onDeactivated } from 'vue'

onActivated(() => {
  // Component became visible again, refresh data if needed
  refreshData()
})

onDeactivated(() => {
  // Component hidden but still alive, pause background work
  pausePolling()
})
</script>
Open in Vue Playground

Full lifecycle for a cached component:

onMounted → onActivated → (user switches away) → onDeactivated
         → (user switches back) → onActivated → ...
         → (cache evicted or parent destroyed) → onUnmounted

KeepAlive with Vue Router ​

vue
<template>
  <router-view v-slot="{ Component, route }">
    <KeepAlive>
      <component :is="Component" :key="route.fullPath" />
    </KeepAlive>
  </router-view>
</template>
Open in Vue Playground

Using route.fullPath as the key means /users/1 and /users/2 are cached separately.

When NOT to use KeepAlive ​

ScenarioWhy
Memory-heavy components (maps, large tables)Cached instances stay in memory
Sensitive data (auth forms, payment flows)Data should be cleared on exit
Components with background timers you can't pauseThey keep running while hidden
Pages where users expect fresh resultsStale cached data confuses users

See also: What are async components? · What are Teleport, Fragments, and Suspense? · How does the is attribute work?

References ​

Released under the MIT License.