Articles
Nuxt Interview Questions: SSR Data Fetching, Hydration, and Request Isolation
Practice Nuxt interview questions with an SSR request timeline covering useFetch, hydration mismatches, and per-request state isolation.
A Nuxt interview question about SSR is rarely answered by saying “useFetch runs on the server.” The useful answer tracks one request, one payload, and one browser hydration: which data was fetched, which cookies reached the server route, what the browser received, and whether another user's request could see the same state. A second trace explains why a page might fetch twice or show a hydration mismatch even though both API calls succeed.
This is an original Nuxt 4 practice scenario, not a claim about a particular employer's interview. Nuxt's data-fetching guide, state-management guidance, and hydration guidance support the framework facts. Nuxt 3 projects may differ in details; check the installed major version before promising an option's behavior. For a transferable UI data exercise, start with Implement list, form, and API rendering with pitfalls.
Interview question: why did the page fetch twice?
Imagine an authenticated orders page. The server receives a request, renders HTML with the order summary, and sends a Nuxt payload to the browser. The browser then hydrates that HTML: it runs the client application against the existing DOM and attaches behavior. The ideal initial trace is server fetch → serialized result in payload → client hydration using that result, without a second equivalent request just to paint the same content.
Nuxt's official guide distinguishes three tools. $fetch is a direct request helper. useFetch wraps $fetch with Nuxt's SSR-aware async-data behavior and automatically derives a key. useAsyncData offers a key and handler when you need custom fetching logic. A plain $fetch('/api/orders') inside isomorphic component setup can run once during server rendering and again during client hydration, because it is just a call in code that executes in both places. useFetch or useAsyncData can serialize the server result into the payload and reuse it on hydration.
Do not turn that into “$fetch is bad.” A user-triggered click in the browser can call $fetch perfectly well. A server-only utility may also use it deliberately. The problem is putting an uncoordinated request in setup when both rendering environments execute the setup. Likewise, seeing two network requests does not automatically prove duplicate hydration: a watcher, a manual refresh(), a changed reactive URL, a different key, or a client-only request can be responsible. Use the browser network trace and server logs to identify the initiator.
A strong answer draws a timeline. At t1 the server calls /api/orders/current and emits HTML plus payload. At t2 the client starts hydration and reads the payload. At t3 a user clicks Refresh and triggers a new request. Two requests across t1 and t3 are intentional; two equivalent requests during t1–t2 may be avoidable. In an interview, say whether you are discussing the first load, a client navigation, or a user action.
When should you choose useFetch or useAsyncData?
For a simple URL, useFetch is the direct choice. It is intended for use in setup, plugins, or route middleware and provides reactive data, status, error, refresh, and clear. For a custom query layer, a non-HTTP source, or multi-step server call, useAsyncData wraps an async handler. In both cases, the handler should return meaningful data. Nuxt's useAsyncData reference says a handler that returns undefined or null may cause a duplicate client request, and it recommends a side-effect-free handler for predictable SSR and hydration.
The key represents the identity of the data, not merely a string that quiets a warning. Consider a route /orders/42 and another route /orders/57. If both use an explicit key 'order' but produce different results in the same app session, they can share reactive data unexpectedly. Include the route identity in a key, or use the automatically generated useFetch key when its inputs already define the request. Nuxt's data-fetching guide also warns that calls with the same explicit key share data, error, and status refs and should use consistent options such as transform and pick. If user identity changes during a browser session, clear or invalidate user-specific keyed data rather than showing the previous account's result.
The options change the interview answer. server: false means no initial server fetch; the data is unavailable until after hydration, so the UI needs a pending or empty state. lazy: true lets client navigation proceed before the result is ready, again requiring loading behavior. pick and transform can reduce what gets serialized into the payload, but they do not avoid the upstream fetch itself. Do not promise that setting lazy makes a slow API fast; it changes when the route waits and how the page handles pending data.
How do request headers and cookies affect SSR?
The browser automatically sends its relevant cookies on same-origin requests. A server-side $fetch does not automatically inherit the original browser request's cookies in every context. Nuxt documents that a relative useFetch on the server uses useRequestFetch to forward appropriate request context and headers; a plain server-side $fetch in a custom handler may need explicit handling. Read the useRequestFetch reference and request-header guidance before changing an authenticated call.
Suppose the server render of /account shows “anonymous,” while the hydrated browser immediately shows Alice. One hypothesis is that the server route did not receive Alice's cookie, but the client request did. Verify the incoming request, the server-side API call, and the resulting payload. Another hypothesis is that the app rendered an auth-dependent value from browser-only storage. Don't assume a hydration bug is a CSS problem or solve it by blindly delaying the entire page until mount.
The response payload goes to the requesting browser. Do not put server secrets, raw access tokens, or another user's private record into data that Nuxt serializes for hydration. Restrict what the page needs, authorize each server request, and check the actual HTML/payload. The framework can keep request state separate; it cannot decide which business fields a user is allowed to see.
Interview question: can one SSR request leak state into another?
Yes, if application code creates mutable shared state at module scope on a long-lived server process. Nuxt's state-management guide explicitly warns against defining an exported ref() outside setup: multiple server requests can share it, creating both leakage and memory-lifetime problems. useState is a Nuxt SSR-friendly state primitive; within a request it is keyed and shared between components, then serialized for hydration. It is not permission to store one user's private state under a process-global key.
Work the original two-user trace. Request A belongs to Alice and sets the selected organization to A. Before A's HTML is complete, request B belongs to Bob and sets organization B. If both touch a module-scoped mutable ref, whichever assignment wins can affect the other response. If each request creates its state within Nuxt's request context, A's render should use A and B's render should use B. The test must overlap requests; a serial test with a process restart between users cannot expose the race.
A compact design rule is: derive data from the current request, keep mutable render state in request-safe composables, and use a shared server cache only when its key, authorization, and eviction policy are explicit. Do not claim that every cache is a leak: public, immutable product data may be safely shared. The dangerous case is a cache key or module variable that ignores user or tenant identity for private data.
Why does hydration report different HTML?
Hydration compares the browser's initial render with the server HTML. A mismatch can come from server/client values that differ: Math.random(), time or locale formatting, browser-only storage, conditional window dimensions, invalid HTML, or a second fetch returning different content. Nuxt's hydration best-practices page recommends SSR-friendly composables and consistent data sources; browser-only behavior belongs in a client-only boundary or after mount when appropriate.
Use evidence in sequence. Capture the server HTML or rendered text, inspect __NUXT_DATA__ or the Nuxt DevTools payload, capture the browser's first render and console warning, and compare the exact field. If the server shows order total 40 but the client first paints 42, ask whether the API changed between two fetches, whether a reactive key was reused, or whether a computed value depends on browser time. If the server and payload both say 40 but a local-storage preference changes the first client render, isolate that preference or initialize it consistently. Hiding every warning with ClientOnly may lose useful SSR content; use the smallest client-only boundary needed.
Error handling belongs in the same trace. If useFetch reports an error, the page can show an error state and offer refresh(). If the route's identity changed, verify the next fetch uses the new identity and does not display stale user data while pending. A good test asserts not just that loading eventually ends, but that account A's content never appears in account B's server HTML or browser first paint.
A request-isolation exercise you can explain aloud
Build a tiny page fixture with two users, Alice and Bob, whose /api/orders/current responses carry distinct IDs. Issue overlapping SSR requests with separate cookies. Parse each HTML response and Nuxt payload. Assert Alice's ID appears only in Alice's response, Bob's only in Bob's, and no module-scoped mutable object is reused. Then hydrate each page and inspect whether an equivalent second API request occurs. Finally navigate to another order ID and verify the async-data key and visible result move together.
This exercise has two independent pass conditions: request isolation and hydration consistency. A single happy-path screenshot proves neither. Repeat with server: false to show why the first HTML needs a pending state, then with an intentionally bad shared key to observe stale data in one browser session. Mark those variations as demonstrations, not as default Nuxt behavior.
None is a report that a company specifically tests Nuxt.
| Question | Nuxt connection | Question to rehearse |
|---|---|---|
| Implement list, form, and API rendering with pitfalls | Initial data and form state | Which values belong in the server response versus browser state? |
| Diagnose Cold-Load and Massive-Table Performance in a Web App | SSR and client work | What would you measure before changing fetch timing? |
| Compare front-end state management approaches | Request-safe state | Which state is per request, per tab, or shared? |
| Design a Debounced Product Search Box | Reactive fetch keys | How are stale results prevented on client navigation? |
| Build a Real-Time Event Feed Dashboard | First render plus live data | How does the initial snapshot hand off to browser updates? |
A concise answer is: “For initial SSR data I use useFetch or a side-effect-free useAsyncData handler, inspect the key and serialized payload, and make loading states explicit when the fetch is client-only or lazy. I keep mutable state request-safe with useState rather than exporting a module-scoped ref. If the page fetches twice or hydrates differently, I compare server HTML, payload, client first render, request headers, and network initiators before changing code.” Use Implement list, form, and API rendering with pitfalls to practice that sequence with a concrete UI.