What Causes This Warning?
This warning occurs when <KeepAlive> contains multiple children or no children. The <KeepAlive> component is designed to cache a single dynamic component at a time.
The Problem
<!-- ❌ Multiple children -->
<template>
<KeepAlive>
<ComponentA />
<ComponentB />
</KeepAlive>
</template>
<!-- ❌ No children -->
<template>
<KeepAlive>
</KeepAlive>
</template>
<!-- ❌ Text or elements instead of component -->
<template>
<KeepAlive>
<div>Not a component</div>
</KeepAlive>
</template>
The Fix
Use with Dynamic Component
<script setup>
import { shallowRef } from 'vue'
import TabA from './TabA.vue'
import TabB from './TabB.vue'
const currentTab = shallowRef(TabA)
</script>
<template>
<!-- ✅ Single dynamic component -->
<KeepAlive>
<component :is="currentTab" />
</KeepAlive>
<button @click="currentTab = TabA">Tab A</button>
<button @click="currentTab = TabB">Tab B</button>
</template>
Use with v-if
<script setup>
import { ref } from 'vue'
const activeTab = ref('home')
</script>
<template>
<!-- ✅ Only one component rendered at a time -->
<KeepAlive>
<HomeTab v-if="activeTab === 'home'" />
<SettingsTab v-else-if="activeTab === 'settings'" />
<ProfileTab v-else-if="activeTab === 'profile'" />
</KeepAlive>
</template>
Use with Vue Router
<template>
<!-- ✅ Keep route components alive -->
<RouterView v-slot="{ Component }">
<KeepAlive>
<component :is="Component" />
</KeepAlive>
</RouterView>
</template>
Common Scenarios
Tab Navigation
<script setup>
import { ref, markRaw } from 'vue'
import Overview from './Overview.vue'
import Details from './Details.vue'
import Settings from './Settings.vue'
const tabs = [
{ name: 'Overview', component: markRaw(Overview) },
{ name: 'Details', component: markRaw(Details) },
{ name: 'Settings', component: markRaw(Settings) },
]
const activeTab = ref(tabs[0])
</script>
<template>
<div class="tabs">
<button
v-for="tab in tabs"
:key="tab.name"
:class="{ active: activeTab === tab }"
@click="activeTab = tab"
>
{{ tab.name }}
</button>
</div>
<!-- ✅ Single cached component -->
<KeepAlive>
<component :is="activeTab.component" />
</KeepAlive>
</template>
Include/Exclude Components
<template>
<!-- Only cache specific components -->
<KeepAlive include="ComponentA,ComponentB">
<component :is="currentComponent" />
</KeepAlive>
<!-- Exclude specific components from cache -->
<KeepAlive exclude="HeavyComponent">
<component :is="currentComponent" />
</KeepAlive>
<!-- Use regex -->
<KeepAlive :include="/Tab.*/">
<component :is="currentComponent" />
</KeepAlive>
</template>
Limit Cache Size
<template>
<!-- Only keep last 5 components in cache -->
<KeepAlive :max="5">
<component :is="currentComponent" />
</KeepAlive>
</template>
With Conditional Rendering
<!-- ❌ Both might render -->
<KeepAlive>
<ComponentA v-show="showA" />
<ComponentB v-show="!showA" />
</KeepAlive>
<!-- ✅ Only one renders -->
<KeepAlive>
<ComponentA v-if="showA" />
<ComponentB v-else />
</KeepAlive>
Lifecycle Hooks with KeepAlive
<script setup>
import { onActivated, onDeactivated } from 'vue'
// Called when component is activated from cache
onActivated(() => {
console.log('Component activated')
// Refresh data, resume animations, etc.
})
// Called when component is deactivated (cached)
onDeactivated(() => {
console.log('Component deactivated')
// Pause animations, clear timers, etc.
})
</script>
Why Only One Child?
<KeepAlive> works by:
- Caching the vnode of its child component
- Restoring it when the child is rendered again
- Managing a single active component at a time
Multiple children would make the caching logic ambiguous.
Quick Checklist
- Use
<component :is>for dynamic components - Use
v-if/v-elsefor conditional components (notv-show) - Ensure only one child component at a time
- Use
include/excludefor selective caching - Set
maxto limit memory usage - Use
onActivated/onDeactivatedfor lifecycle hooks