Files
agent-skills/skills/vue-best-practices/reference/suspense-nesting-order-with-router.md
Jason Woltje f5792c40be feat: Complete fleet — 94 skills across 10+ domains
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>
2026-02-16 16:27:42 -06:00

130 lines
3.6 KiB
Markdown

# Correct Nesting Order: RouterView, Transition, KeepAlive, Suspense
## Rule
When combining `<Suspense>` with `<Transition>`, `<KeepAlive>`, and `<RouterView>`, the nesting order must be: `RouterView` -> `Transition` -> `KeepAlive` -> `Suspense`. Incorrect nesting causes components to not work together properly.
## Why This Matters
Each of these components wraps and controls its child in specific ways. Incorrect nesting leads to:
- Transitions not animating
- Components not being cached by KeepAlive
- Suspense not catching async dependencies
- Subtle bugs that are hard to diagnose
## Bad Code
```vue
<template>
<!-- Wrong order - Suspense wrapping KeepAlive -->
<RouterView v-slot="{ Component }">
<Suspense>
<KeepAlive>
<Transition mode="out-in">
<component :is="Component" />
</Transition>
</KeepAlive>
</Suspense>
</RouterView>
</template>
```
```vue
<template>
<!-- Wrong - KeepAlive outside Transition -->
<RouterView v-slot="{ Component }">
<KeepAlive>
<Transition mode="out-in">
<Suspense>
<component :is="Component" />
</Suspense>
</Transition>
</KeepAlive>
</RouterView>
</template>
```
## Good Code
```vue
<template>
<RouterView v-slot="{ Component }">
<template v-if="Component">
<Transition mode="out-in">
<KeepAlive>
<Suspense>
<!-- Main content -->
<component :is="Component" />
<!-- Loading state -->
<template #fallback>
<div class="route-loading">
Loading...
</div>
</template>
</Suspense>
</KeepAlive>
</Transition>
</template>
</RouterView>
</template>
```
## With Selective KeepAlive
```vue
<template>
<RouterView v-slot="{ Component, route }">
<template v-if="Component">
<Transition :name="route.meta.transition || 'fade'" mode="out-in">
<KeepAlive :include="cachedViews">
<Suspense :timeout="200">
<component :is="Component" :key="route.fullPath" />
<template #fallback>
<RouteLoadingSkeleton :route="route" />
</template>
</Suspense>
</KeepAlive>
</Transition>
</template>
</RouterView>
</template>
<script setup>
import { computed } from 'vue'
import { useRoute } from 'vue-router'
const route = useRoute()
// Only cache specific routes
const cachedViews = computed(() => ['Dashboard', 'Profile', 'Settings'])
</script>
```
## Why This Order?
1. **RouterView** - Provides the route component via scoped slot
2. **Transition** - Needs to wrap what animates (the cached/suspended content)
3. **KeepAlive** - Caches the Suspense + component together
4. **Suspense** - Directly wraps the async component to handle loading
## Important Notes
- Vue Router's lazy-loaded route components (via dynamic imports) don't trigger Suspense directly
- Only async components within the route component trigger Suspense
- Use `v-if="Component"` to handle the case when no route matches
- The `:key` on the component can force re-render and re-trigger Suspense
## Key Points
1. Always follow the order: `RouterView` -> `Transition` -> `KeepAlive` -> `Suspense`
2. Each wrapper serves a specific purpose in this hierarchy
3. Use `mode="out-in"` on Transition to prevent overlap during route changes
4. Consider using route-based keys for fine-grained control over when Suspense triggers
## References
- [Vue.js Suspense Documentation](https://vuejs.org/guide/built-ins/suspense#combining-with-other-components)
- [Vue Router Transitions](https://router.vuejs.org/guide/advanced/transitions)