nuxt4-patterns
affaan-m/ecc
Nuxt 4 patterns for SSR hydration, route rules, lazy loading, and safe data fetching.
What is nuxt4-patterns?
Patterns and best practices for building Nuxt 4 apps with server-side rendering, hybrid rendering strategies, and client-server data synchronization. Use this when debugging hydration mismatches, configuring route-level rendering rules, optimizing performance, or implementing SSR-safe data fetching with useFetch and useAsyncData.
- Prevent hydration mismatches by keeping first renders deterministic and moving browser-only logic behind onMounted, import.meta.client, or ClientOnly
- Configure route-level rendering strategies (prerender, SWR, ISR, client-only) via routeRules in nuxt.config.ts
- Implement SSR-safe data fetching with useFetch and useAsyncData, forwarding server data into the Nuxt payload to avoid double-fetches
- Optimize performance with lazy loading, lazy hydration, and payload size trimming using pick and shallow reactivity
- Handle lazy-loaded non-critical data with explicit loading UI and status checks
How to install nuxt4-patterns
npx skills add null --skill nuxt4-patternsHow to use nuxt4-patterns
- 1.Review the hydration safety section and audit your components for Date.now(), Math.random(), or browser-only APIs in SSR-rendered templates
- 2.Replace top-level $fetch calls with await useFetch() or useAsyncData() in pages and components to enable server-side data forwarding
- 3.Define routeRules in nuxt.config.ts for each route group (prerender, swr, isr, ssr: false, cache) based on SEO and freshness needs
- 4.Wrap non-critical components with the Lazy prefix and conditionally render them with v-if to defer chunk loading
- 5.Use the review checklist to verify first SSR and hydrated renders match, data fetching is safe, and route rules align with requirements
Use cases
- Debugging hydration mismatches between server HTML and client state in SSR apps
- Setting rendering strategy per route group (marketing pages prerendered, APIs cached, admin dashboards client-only)
- Fetching page data safely during SSR and hydration without duplicate requests or client-side race conditions
- Lazy-loading below-the-fold components and interactive islands to reduce initial payload and improve Core Web Vitals
- Configuring incremental static regeneration (ISR) or stale-while-revalidate (SWR) for frequently-accessed content
- Nuxt 4 app developers building SSR or hybrid-rendered applications
- Full-stack engineers optimizing performance and SEO for server-rendered pages
- Teams managing route-level rendering and caching strategies across marketing, product, and API routes
nuxt4-patterns FAQ
useFetch is for simple $fetch() calls and automatically forwards server-fetched data into the Nuxt payload. useAsyncData is for custom fetchers, multiple async sources, or when you need a stable cache key. Both are SSR-safe when awaited at the top level.
Use lazy: true for non-critical data that should not block navigation. The component must handle status === 'pending' in the UI and display a loading state while data is being fetched.
Keep the first SSR render deterministic by avoiding Date.now(), Math.random(), and browser APIs in templates. Move browser-only logic behind onMounted(), import.meta.client, ClientOnly, or .client.vue components so the server and client produce identical markup.
Use swr (stale-while-revalidate) with a maxAge like 3600 seconds to serve cached content instantly while revalidating in the background. Use isr for incremental static regeneration on supported platforms, or prerender if the catalog is small and rarely changes.
Yes, Nuxt code-splits by route automatically. Use the Lazy prefix for non-critical components and conditionally render them with v-if to further optimize. For custom strategies, use defineLazyHydrationComponent() with visibility or idle detection.
Full instructions (SKILL.md)
Source of truth, from affaan-m/ecc.
name: nuxt4-patterns description: Nuxt 4 app patterns for hydration safety, performance, route rules, lazy loading, and SSR-safe data fetching with useFetch and useAsyncData. metadata: origin: ECC
Nuxt 4 Patterns
Use when building or debugging Nuxt 4 apps with SSR, hybrid rendering, route rules, or page-level data fetching.
When to Activate
- Hydration mismatches between server HTML and client state
- Route-level rendering decisions such as prerender, SWR, ISR, or client-only sections
- Performance work around lazy loading, lazy hydration, or payload size
- Page or component data fetching with
useFetch,useAsyncData, or$fetch - Nuxt routing issues tied to route params, middleware, or SSR/client differences
Hydration Safety
- Keep the first render deterministic. Do not put
Date.now(),Math.random(), browser-only APIs, or storage reads directly into SSR-rendered template state. - Move browser-only logic behind
onMounted(),import.meta.client,ClientOnly, or a.client.vuecomponent when the server cannot produce the same markup. - Use Nuxt's
useRoute()composable, not the one fromvue-router. - Do not use
route.fullPathto drive SSR-rendered markup. URL fragments are client-only, which can create hydration mismatches. - Treat
ssr: falseas an escape hatch for truly browser-only areas, not a default fix for mismatches.
Data Fetching
- Prefer
await useFetch()for SSR-safe API reads in pages and components. It forwards server-fetched data into the Nuxt payload and avoids a second fetch on hydration. - Use
useAsyncData()when the fetcher is not a simple$fetch()call, when you need a custom key, or when you are composing multiple async sources. - Give
useAsyncData()a stable key for cache reuse and predictable refresh behavior. - Keep
useAsyncData()handlers side-effect free. They can run during SSR and hydration. - Use
$fetch()for user-triggered writes or client-only actions, not top-level page data that should be hydrated from SSR. - Use
lazy: true,useLazyFetch(), oruseLazyAsyncData()for non-critical data that should not block navigation. Handlestatus === 'pending'in the UI. - Use
server: falseonly for data that is not needed for SEO or the first paint. - Trim payload size with
pickand prefer shallower payloads when deep reactivity is unnecessary.
const route = useRoute()
const { data: article, status, error, refresh } = await useAsyncData(
() => `article:${route.params.slug}`,
() => $fetch(`/api/articles/${route.params.slug}`),
)
const { data: comments } = await useFetch(`/api/articles/${route.params.slug}/comments`, {
lazy: true,
server: false,
})
Route Rules
Prefer routeRules in nuxt.config.ts for rendering and caching strategy:
export default defineNuxtConfig({
routeRules: {
'/': { prerender: true },
'/products/**': { swr: 3600 },
'/blog/**': { isr: true },
'/admin/**': { ssr: false },
'/api/**': { cache: { maxAge: 60 * 60 } },
},
})
prerender: static HTML at build timeswr: serve cached content and revalidate in the backgroundisr: incremental static regeneration on supported platformsssr: false: client-rendered routecacheorredirect: Nitro-level response behavior
Pick route rules per route group, not globally. Marketing pages, catalogs, dashboards, and APIs usually need different strategies.
Lazy Loading and Performance
- Nuxt already code-splits pages by route. Keep route boundaries meaningful before micro-optimizing component splits.
- Use the
Lazyprefix to dynamically import non-critical components. - Conditionally render lazy components with
v-ifso the chunk is not loaded until the UI actually needs it. - Use lazy hydration for below-the-fold or non-critical interactive UI.
<template>
<LazyRecommendations v-if="showRecommendations" />
<LazyProductGallery hydrate-on-visible />
</template>
- For custom strategies, use
defineLazyHydrationComponent()with a visibility or idle strategy. - Nuxt lazy hydration works on single-file components. Passing new props to a lazily hydrated component will trigger hydration immediately.
- Use
NuxtLinkfor internal navigation so Nuxt can prefetch route components and generated payloads.
Review Checklist
- First SSR render and hydrated client render produce the same markup
- Page data uses
useFetchoruseAsyncData, not top-level$fetch - Non-critical data is lazy and has explicit loading UI
- Route rules match the page's SEO and freshness requirements
- Heavy interactive islands are lazy-loaded or lazily hydrated
Related skills
More from affaan-m/ecc and the wider catalog.
openclaw-persona-forge
Forge complete OpenClaw lobster personas with guided design or gacha randomization, generating SOUL.md, identity rules, names, and avatar prompts.
opensource-pipeline
Fork, sanitize, and package private projects for safe public release through a 3-stage pipeline.
orch-add-feature
Agent skill from affaan-m/ecc.
orch-build-mvp
Agent skill from affaan-m/ecc.
orch-change-feature
Agent skill from affaan-m/ecc.
orch-fix-defect
Agent skill from affaan-m/ecc.