Mixing Static and Dynamic Rendering in Nuxt 3 with Route Rules
Nuxt 3 route rules let you declare prerendering, SSR, and caching per route in nuxt.config.ts. Learn how to choose a strategy, verify the build output, and avoid stale-cache surprises.
28 Jul 2025, 12:34 UTC

The problem: one codebase, two very different page types
Most Nuxt projects eventually hit a fork in the road. A marketing landing page wants to be fast and cheap to serve. An account dashboard needs fresh, user-specific data on every request. If you pick a single global strategy, you either pay for server-side rendering on pages that never change, or you ship stale content to authenticated users.
Nuxt 3 route rules solve this by letting you declare a rendering strategy per route pattern in nuxt.config.ts. The rules are resolved by Nitro at build time, so the same codebase can emit static HTML for public pages and server-rendered responses for private ones.
What route rules actually control
Route rules map glob patterns to rendering directives. The most common keys are:
prerender: true— generates static HTML at build time for matching routes.ssr: false— disables server rendering for the route, falling back to client-side rendering.swrandisr— serve a cached response immediately and regenerate it in the background, reducing origin load.
Because these are resolved at build time by Nitro, the output is baked into your deployment artifact rather than decided at runtime by application code.
A worked example: blog, account, and catalog routes
A practical pattern is to default to SSR for safety, then opt specific routes into static generation. The following nuxt.config.ts block shows one way to split a typical application:
// nuxt.config.ts
export default defineNuxtConfig({
routeRules: {
// Public marketing pages: prebuild as static HTML
'/': { prerender: true },
'/about': { prerender: true },
// Blog: prerender all posts, but allow background regeneration
// Verify the exact swr key and TTL format against your installed Nitro version
'/blog/**': { prerender: true, swr: 3600 },
// Authenticated area: keep server-rendered for fresh, user-specific data
'/account/**': { ssr: true },
// Large product catalog: cache and revalidate in background
// Confirm whether your provider uses swr or isr and what units it expects
'/products/**': { swr: true }
}
})
In this setup, /blog/** prebuilds every blog route at deploy time. The /account/** routes stay server-rendered so user-specific data remains fresh. The /products/** rule uses a stale-while-revalidate style cache to reduce origin load, though the exact key and time-to-live format depend on your Nitro version and deployment provider.
Where to run this: edit nuxt.config.ts in your project root, then run nuxi build locally or in your CI pipeline. No special permissions are required beyond normal file-system access to the repository.
Trade-offs and limitations
Build time scales with prerendered routes
Prerendering many dynamic routes can significantly increase build times and memory usage, especially if slugs are generated from a large CMS dataset. If your blog has thousands of posts, the build step may become a bottleneck before any caching benefit is realized.
Cache invalidation is not automatic
If your CMS updates content, a prerendered or SWR-cached route will not know about it. You need a webhook or deploy hook to rebuild the affected routes or purge the cache. Without that pipeline, users may see stale content until the next deployment or background regeneration window closes.
Route rules are route-level, not component-level
You cannot mix strategies inside a single page. If half of a page is static and half is dynamic, you must either split the route or fetch the dynamic portion client-side after hydration.
Version sensitivity
Route rules are version-sensitive because Nitro presets and caching semantics have evolved across Nuxt 3 minor releases. Always confirm the exact keys and provider behavior against the version installed in your project before relying on them in production.
Verifying that the rules took effect
After editing nuxt.config.ts, confirm the strategy with a few checks. Run the following from your project root; normal file-system permissions are sufficient.
# Build the application
npx nuxi build
# List generated output to see which routes emitted HTML files
ls dist/
# Inspect response headers for a sample route
# Replace <preview-domain> with your actual deployment URL
curl -I https://<preview-domain>/blog/example-post
Expected checks: prerendered routes should appear as static HTML files under dist/. The curl output should show caching headers such as cache-control or age when a cache rule is active, though exact header names vary by provider.
Relevant risks: if a prerendered route is missing from dist/, the glob pattern may not match the generated path. If expected headers are absent in a preview deployment, the route rule may not be supported by your current Nitro preset or provider.
To test the client-side fallback, temporarily set a route to ssr: false and load it in a browser. Confirm there are no hydration errors in the console. If hydration fails, the page likely depends on server-only context that is unavailable during client rendering.
Start conservative, then opt in
Route rules are a deployment-time decision, not a runtime toggle. The safest workflow is to leave your application defaulting to SSR, identify the routes that are genuinely public and cacheable, and move them to prerender or swr one at a time. Before you commit, verify the build output and preview headers for that specific route. If your content changes frequently, plan the invalidation webhook before you enable caching—otherwise you are trading build complexity for a staleness problem you cannot easily undo.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.