Files
agent-skills/skills/vue-best-practices/reference/keepalive-router-fresh-vs-cached.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

5.7 KiB

title, impact, impactDescription, type, tags
title impact impactDescription type tags
KeepAlive Router Navigation Fresh vs Cached Problem MEDIUM When using KeepAlive with Vue Router, users may get cached pages when they expect fresh content gotcha
vue3
keepalive
vue-router
navigation
cache
ux

KeepAlive Router Navigation Fresh vs Cached Problem

Impact: MEDIUM - When using KeepAlive with Vue Router, navigation from menus or breadcrumbs may show cached (stale) content when users expect a fresh page. This creates confusing UX where the page appears "stuck" on old data.

Task Checklist

  • Define clear rules for when to use cached vs fresh pages
  • Use route keys strategically to control freshness
  • Implement onActivated to refresh stale data
  • Consider navigation source when deciding cache behavior

The Problem

<!-- App.vue -->
<template>
  <nav>
    <router-link to="/products">Products</router-link>
  </nav>

  <router-view v-slot="{ Component }">
    <KeepAlive>
      <component :is="Component" />
    </KeepAlive>
  </router-view>
</template>

Scenario:

  1. User visits /products?category=shoes - sees shoes
  2. User navigates to /products?category=hats - sees hats
  3. User clicks "Products" nav link (to /products)
  4. Expected: Fresh products page or default category
  5. Actual: Still shows hats (cached state)!

Users clicking navigation expect a "fresh start" but get the cached state.

Solutions

Solution 1: Use Route Full Path as Key

<template>
  <router-view v-slot="{ Component, route }">
    <KeepAlive>
      <!-- Different query params = different cache entry -->
      <component :is="Component" :key="route.fullPath" />
    </KeepAlive>
  </router-view>
</template>

Tradeoff: Creates separate cache entry for each unique URL. May increase memory usage.

Solution 2: Refresh Data on Activation

<!-- Products.vue -->
<script setup>
import { ref, onActivated, watch } from 'vue'
import { useRoute } from 'vue-router'

const route = useRoute()
const products = ref([])
const lastParams = ref(null)

async function fetchProducts() {
  const params = route.query
  products.value = await api.getProducts(params)
  lastParams.value = JSON.stringify(params)
}

// Initial fetch
fetchProducts()

// Refresh when re-activated if params changed
onActivated(() => {
  const currentParams = JSON.stringify(route.query)
  if (currentParams !== lastParams.value) {
    fetchProducts()
  }
})

// Also watch for changes while component is active
watch(() => route.query, fetchProducts, { deep: true })
</script>

Solution 3: Navigation-Aware Cache Control

Different behavior based on how user navigated:

<script setup>
import { ref, onActivated } from 'vue'
import { useRoute, useRouter, onBeforeRouteUpdate } from 'vue-router'

const route = useRoute()
const router = useRouter()
const shouldRefresh = ref(false)

// Mark for refresh when coming from nav link (no query params)
onBeforeRouteUpdate((to, from) => {
  // If navigating to base path without params, user wants fresh view
  if (Object.keys(to.query).length === 0 && Object.keys(from.query).length > 0) {
    shouldRefresh.value = true
  }
})

onActivated(() => {
  if (shouldRefresh.value) {
    resetToDefaultState()
    shouldRefresh.value = false
  }
})

function resetToDefaultState() {
  // Reset filters, clear search, show default view
}
</script>

Solution 4: Don't Cache Route-Dependent Pages

<script setup>
// Don't cache pages where query params significantly change content
const noCacheRoutes = ['ProductSearch', 'SearchResults', 'FilteredList']
</script>

<template>
  <router-view v-slot="{ Component, route }">
    <KeepAlive :exclude="noCacheRoutes">
      <component :is="Component" />
    </KeepAlive>
  </router-view>
</template>

Solution 5: Use Route Meta for Fresh Navigation

// router.js
const routes = [
  {
    path: '/products',
    component: Products,
    meta: {
      keepAlive: true,
      refreshOnDirectNavigation: true
    }
  }
]
<!-- App.vue -->
<script setup>
import { watch } from 'vue'
import { useRoute } from 'vue-router'

const route = useRoute()
const forceRefreshKey = ref(0)

// Watch for navigation to routes that want fresh state
watch(
  () => route.fullPath,
  (newPath, oldPath) => {
    // Direct navigation to base path = user wants fresh
    if (route.meta.refreshOnDirectNavigation && !route.query.length) {
      forceRefreshKey.value++
    }
  }
)
</script>

<template>
  <router-view v-slot="{ Component }">
    <KeepAlive>
      <component
        :is="Component"
        :key="`${route.name}-${forceRefreshKey}`"
      />
    </KeepAlive>
  </router-view>
</template>

Best Practice: Be Explicit About Cache Behavior

Document your caching rules:

// cacheRules.js
export const CACHE_RULES = {
  // Always cached - static content, user preferences
  ALWAYS: ['Dashboard', 'Settings', 'Profile'],

  // Never cached - dynamic search/filter results
  NEVER: ['SearchResults', 'FilteredProducts'],

  // Cached but refreshes on activation
  STALE_WHILE_REVALIDATE: ['Notifications', 'Messages']
}

Key Points

  1. User expectation mismatch - Nav links often imply "fresh" but get cached
  2. Use fullPath key carefully - Prevents reuse but increases memory
  3. Implement onActivated refresh - Check if data needs updating
  4. Don't cache filter/search pages - These are highly query-dependent
  5. Document cache behavior - Make rules explicit for your team

Reference