Pulled ALL skills from 15 source repositories: - anthropics/skills: 16 (docs, design, MCP, testing) - obra/superpowers: 14 (TDD, debugging, agents, planning) - coreyhaines31/marketingskills: 25 (marketing, CRO, SEO, growth) - better-auth/skills: 5 (auth patterns) - vercel-labs/agent-skills: 5 (React, design, Vercel) - antfu/skills: 16 (Vue, Vite, Vitest, pnpm, Turborepo) - Plus 13 individual skills from various repos Mosaic Stack is not limited to coding — the Orchestrator and subagents serve coding, business, design, marketing, writing, logistics, analysis, and more. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
5.5 KiB
title, impact, impactDescription, type, tags
| title | impact | impactDescription | type | tags | ||||||
|---|---|---|---|---|---|---|---|---|---|---|
| Use KeepAlive to Preserve Dynamic Component State | MEDIUM | Dynamic component switching destroys and recreates components, losing all internal state unless wrapped in KeepAlive | best-practice |
|
Use KeepAlive to Preserve Dynamic Component State
Impact: MEDIUM - When switching between components using <component :is="...">, Vue destroys the old component and creates a new one. All internal state (form inputs, scroll position, fetched data) is lost. Wrapping dynamic components in <KeepAlive> caches them and preserves their state.
Task Checklist
- Wrap
<component :is>with<KeepAlive>when state preservation is needed - Use
includeandexcludeto control which components are cached - Use
maxto limit cache size and prevent memory issues - Implement
onActivated/onDeactivatedhooks for cache-aware logic - Consider NOT using KeepAlive when fresh state is desired
The Problem: State Loss
<script setup>
import { ref, shallowRef } from 'vue'
import TabA from './TabA.vue'
import TabB from './TabB.vue'
const currentTab = shallowRef(TabA)
</script>
<template>
<button @click="currentTab = TabA">Tab A</button>
<button @click="currentTab = TabB">Tab B</button>
<!-- State is lost when switching tabs! -->
<component :is="currentTab" />
</template>
If TabA has a form with user input, switching to TabB and back resets all input.
Solution: KeepAlive
<template>
<button @click="currentTab = TabA">Tab A</button>
<button @click="currentTab = TabB">Tab B</button>
<!-- State is preserved when switching -->
<KeepAlive>
<component :is="currentTab" />
</KeepAlive>
</template>
Now TabA's state persists even when TabB is displayed.
Controlling What Gets Cached
Include/Exclude by Name
Only cache specific components:
<template>
<!-- Only cache components named 'TabA' or 'TabB' -->
<KeepAlive include="TabA,TabB">
<component :is="currentTab" />
</KeepAlive>
<!-- Cache all except 'HeavyComponent' -->
<KeepAlive exclude="HeavyComponent">
<component :is="currentTab" />
</KeepAlive>
<!-- Using regex -->
<KeepAlive :include="/^Tab/">
<component :is="currentTab" />
</KeepAlive>
<!-- Using array -->
<KeepAlive :include="['TabA', 'TabB', 'Settings']">
<component :is="currentTab" />
</KeepAlive>
</template>
Important: Components must have a name option to be matched:
<!-- TabA.vue -->
<script>
export default {
name: 'TabA' // Required for include/exclude matching
}
</script>
<script setup>
// ...composition API code
</script>
Or in Vue 3.3+ with <script setup>:
<script setup>
defineOptions({
name: 'TabA'
})
</script>
Limit Cache Size
Prevent memory issues with many cached components:
<template>
<!-- Only keep last 5 components in cache -->
<KeepAlive :max="5">
<component :is="currentTab" />
</KeepAlive>
</template>
When cache exceeds max, the least recently accessed component is destroyed.
Lifecycle Hooks: onActivated and onDeactivated
Cached components need special lifecycle hooks:
<!-- TabA.vue -->
<script setup>
import { onMounted, onUnmounted, onActivated, onDeactivated } from 'vue'
// Only called on first mount, NOT when switching back
onMounted(() => {
console.log('TabA mounted (once)')
})
// Only called when truly destroyed, NOT when switching away
onUnmounted(() => {
console.log('TabA unmounted')
})
// Called EVERY time component becomes visible
onActivated(() => {
console.log('TabA activated')
// Refresh data, resume timers, etc.
fetchLatestData()
})
// Called EVERY time component is hidden (but kept in cache)
onDeactivated(() => {
console.log('TabA deactivated')
// Pause timers, save draft, etc.
pauseAutoRefresh()
})
</script>
Common use cases for activation hooks:
- Refresh stale data when returning to a tab
- Resume/pause video or audio playback
- Reconnect/disconnect WebSocket connections
- Save/restore scroll position
- Track analytics for tab views
KeepAlive with Vue Router
For route-based caching:
<!-- App.vue -->
<template>
<router-view v-slot="{ Component }">
<KeepAlive include="Dashboard,Settings">
<component :is="Component" />
</KeepAlive>
</router-view>
</template>
With transition:
<template>
<router-view v-slot="{ Component }">
<Transition name="fade" mode="out-in">
<KeepAlive>
<component :is="Component" :key="$route.fullPath" />
</KeepAlive>
</Transition>
</router-view>
</template>
When NOT to Use KeepAlive
Don't cache when:
<!-- Fresh data needed each time -->
<template>
<!-- NO KeepAlive - want fresh search results each visit -->
<component :is="currentView" />
</template>
- Form should reset between visits
- Data must be fresh (real-time dashboards)
- Component has significant memory footprint
- Security-sensitive data should be cleared
Performance Considerations
<script setup>
import { shallowRef } from 'vue'
import TabA from './TabA.vue'
import TabB from './TabB.vue'
// Use shallowRef for component references
// Regular ref would deeply track the component object unnecessarily
const currentTab = shallowRef(TabA)
</script>