Routing questions test whether you understand that Nuxt routes exist in two places: Vue Router in the app, and Nitro's route rules on the server. Middleware running on the server for the first request and on the client afterwards, a redirect that must set a status code, a protected route that is "protected" only in the browser: these are the scenarios interviewers build questions around.
app/pages/index.vue → /, about.vue → /about, users/[id].vue → /users/:id, [...slug].vue catch-all, [[id]].vue optional param, (marketing)/pricing.vue route groups that organise files without affecting the URL. A users.vue next to a users/ folder is a parent route that renders children through <NuxtPage>.definePageMeta is compiled out of the component and attached to the route record: layout, middleware, name, path, alias, redirect, keepalive, key, validate (return false for a 404 or an error object), scrollToTop, pageTransition/layoutTransition. It cannot reference component-local variables because it is extracted at build time.app/layouts/, wrap pages through <NuxtLayout> in app.vue, and are chosen per page (layout: 'admin') or switched at runtime with setPageLayout. layout: false renders the page bare.app/middleware/): named files are opted into per page, *.global.ts run on every navigation in alphabetical order, inline functions go straight into definePageMeta. They run on the server for the initial request and in the browser for client-side navigations, return navigateTo(...) to redirect or abortNavigation(error) to stop, and receive to/from. Plugins can add middleware dynamically with addRouteMiddleware./admin protects nothing; the API must check the session itself. Middleware exists so users do not see a page they cannot use.navigateTo(to, { replace, redirectCode, external, open }) works in middleware, plugins and components; on the server it sets the status (default 302; use redirectCode: 301 for permanent). external: true is required for off-site URLs, which is also the guard against open redirects.<NuxtLink> renders <a> with client-side navigation and prefetches the target page's chunk and payload when the link enters the viewport (prefetchOn: 'visibility' default, or 'interaction'; noPrefetch to opt out). It handles external links, target, active classes and trailing slashes; useRouter().push is for programmatic navigation.experimental.typedPages, default in Nuxt 5 Nuxt 5 ) generate a route map with unplugin-vue-router, so navigateTo({ name: 'users-id', params: { id } }) and useRoute('users-id') are type-checked.redirect, headers, prerender, swr, isr, ssr: false, cors, cache, appMiddleware. They apply before the Vue app exists, so a redirect rule answers without rendering. Hybrid rendering is configured here, see rendering strategies.app/router.options.ts customises scrollBehavior; <NuxtPage :page-key> or definePageMeta({ key }) controls when a page component is remounted versus reused when only params change.Auth middleware done properly: the server-side check redirects with a status code, the page opts in, and the API stays the real gate:
export default defineNuxtRouteMiddleware((to) => {
const { loggedIn } = useUserSession() // nuxt-auth-utils, works on server and client
if (!loggedIn.value) {
return navigateTo({ path: '/login', query: { redirect: to.fullPath } }, { redirectCode: 302 })
}
})
<script setup lang="ts">
definePageMeta({
middleware: 'auth',
layout: 'account',
// 404 for anything that is not a numeric id on sibling routes: validate runs before the page renders
})
</script>
export default defineEventHandler(async (event) => {
const { user } = await requireUserSession(event) // 401 if missing: this is the actual protection
return useDb().orders.forUser(user.id)
})
A parent route with nested children, and a link that prefetches on hover rather than on sight:
<template>
<div class="grid grid-cols-[16rem_1fr]">
<nav>
<NuxtLink
v-for="u in users"
:key="u.id"
:to="{ name: 'users-id', params: { id: u.id } }"
prefetch-on="interaction"
active-class="font-semibold"
>{{ u.name }}</NuxtLink>
</nav>
<!-- users/[id].vue renders here -->
<NuxtPage />
</div>
</template>
Route rules for the parts of the site that should not be rendered per request:
export default defineNuxtConfig({
routeRules: {
'/': { prerender: true },
'/blog/**': { swr: 600 }, // rendered once, served stale for 10 minutes, revalidated in the background
'/admin/**': { ssr: false, appMiddleware: 'auth' }, // SPA area, middleware attached without touching the pages
'/old-docs/**': { redirect: { to: '/docs/**', status: 301 } },
'/api/public/**': { cors: true, headers: { 'cache-control': 'public, max-age=60' } },
},
})
Middleware runs in the browser after the first load. Anyone can skip it with fetch('/api/admin/users'). Every protected server route must verify the session itself; middleware only decides what the user sees.
It is extracted at compile time, so definePageMeta({ layout: isAdmin ? 'admin' : 'default' }) fails. Use setPageLayout at runtime, or middleware that reads the route.
The prefetchOn values and defaults, plus any 4.5 additions to NuxtLink (such as named views and useLayout() mentioned in the 4.5 release notes). Confirm before naming them.
users.vue with nested users/[id].vue, add validate that 404s on non-numeric ids, and confirm the 404 status with curl -I.to.path with import.meta.server ? 'server' : 'client'; load a page directly, then navigate to it, and compare.routeRules above, run nuxt build and node .output/server/index.mjs, and check response headers on /blog/x twice to see the cache behaviour."Route middleware runs before a navigation resolves: on the server for the very first request and in the browser for every client-side navigation afterwards. Global middleware runs on every route in alphabetical order, named middleware is opted into per page through definePageMeta, and it can redirect with navigateTo, including a proper status code on the server, or stop with abortNavigation. Its job is user experience: sending an anonymous user to the login page, or picking a layout. It is not authorisation, because in the browser it can be bypassed with a direct API call, so every protected server route checks the session itself."
"Middleware is Vue Router-level and runs in the Vue app on both sides of the wire; route rules are Nitro-level configuration keyed by path pattern that applies before the app exists. So a redirect in route rules answers instantly with no render, headers and caching (swr, isr, prerender) are route rules, and turning a section of the site into an SPA with ssr: false is a route rule too. Route rules can also attach middleware to a path pattern with appMiddleware, which is handy for layers. I reach for route rules whenever the decision does not need application state."
Nothing yet. Notes filed here show up on this page and the section overview.
Building Nuxt apps
The application developer's map of Nuxt 4 — directory conventions, the request lifecycle, and how routing, data fetching, state, plugins, errors, SEO and rendering strategies fit together.
Data fetching
useFetch, useAsyncData and $fetch, the payload and why data is not fetched twice, keys, options, caching with getCachedData, forwarding headers, interceptors and error handling.