This page is mostly a lookup table, but it is the one to internalise: in an interview you will be given a symptom, and you get credit for reaching for the right instrument before the right fix.
| Layer | Symptom the user reports | Instrument | Typical levers |
|---|---|---|---|
| Build / dev | "nuxt dev takes forever", "HMR reloads the whole page" | hyperfine 'pnpm nuxt build', DEBUG=nuxt:*, node --cpu-prof node_modules/nuxt/bin/nuxt.mjs build, nuxt analyze | cheap module setup, cache derived work under buildDir, vite.optimizeDeps, fewer global components and auto-imports |
| Server (TTFB) | "the first byte is slow", "it is fine locally" | curl -s -o /dev/null -w '%{time_starttransfer}\n', autocannon, node --cpu-prof, a Server-Timing header | parallel data fetching, lazy, route rules (swr, isr, prerender), cached handlers, smaller payload |
| Network | "it is slow on 4G", "huge HTML" | Network panel, curl -so /dev/null -w '%{size_download}', DevTools → Payload | shrink runtimeConfig.public, pick/transform, compression, image and font modules, fewer third-party scripts |
| Client | "slow to become interactive", "layout jumps" | Lighthouse, CrUX, Performance panel, web-vitals in a plugin | code splitting, Lazy* with hydration strategies, server components, shallowRef/markRaw, deferred third-party scripts |
| Cost / scale | "the bill", "pods restart under load" | autocannon throughput, cache hit ratio, process.memoryUsage() | caching layer, edge rendering, prerendering, fixing the leak first (memory) |
Four commands worth having in muscle memory:
# TTFB on a warm, built server (run it a dozen times, look at the spread, not one number)
curl -s -o /dev/null -w 'ttfb=%{time_starttransfer}s total=%{time_total}s size=%{size_download}B\n' http://localhost:3000/
# Throughput and latency percentiles under load
npx autocannon -c 20 -d 30 http://localhost:3000/
# Build time with and without a change, repeated and averaged
hyperfine --warmup 1 --runs 5 'pnpm nuxt build'
# Where the client bundle went
pnpm nuxt analyze
A Server-Timing header turns the browser's Network panel into a server profiler, which is the fastest way to show a team where the time goes:
export default defineNitroPlugin((nitroApp) => {
nitroApp.hooks.hook('request', (event) => {
event.context.startedAt = performance.now()
})
nitroApp.hooks.hook('beforeResponse', (event) => {
const started = event.context.startedAt as number | undefined
if (!started) return
setResponseHeader(event, 'Server-Timing', `total;dur=${(performance.now() - started).toFixed(1)}`)
})
})
Cold caches, JIT warm-up and a laptop throttling under load all produce numbers that look like wins. Warm up, repeat, and compare distributions. hyperfine and autocannon do this for you; a single curl does not.
Server-Timing plugin above into your layer behind an option that defaults to off. Read the timing in DevTools → Network → Timing."I ask what they did and where, because 'slow' is four different problems: slow to first byte, slow to load, slow to become interactive, or janky once loaded. Then I measure that specific layer on a built server, not in dev: curl timings and autocannon for the server, Lighthouse and the Performance panel for the client, nuxt analyze for the bundle. Only once I can name the layer do I pick a lever, and I re-run the same measurement afterwards so I can quote the delta rather than claim an improvement."
Performance
Where the time actually goes in a Nuxt app, the senior answer shape, and why a layer author's first performance question is what the layer costs every consuming app.
Build and dev performance
What module setup, pre-bundling and template generation cost your teammates, how to measure build time properly, and the levers that actually move it.