ExpertArchitecture
For a product catalogue, a logged-in dashboard and a checkout, which rendering strategy would you choose for each and why?
Catalogue: static with revalidation. The content is identical for everyone and changes on a business cadence, so serve from the CDN and revalidate on a timer or on publish via a tag. It is the fastest possible option and survives an API outage. Dashboard: dynamic server rendering, because the content is per-user and must not be cached at a shared layer — render on request, stream the shell early with Suspense so the slow widgets do not hold up first paint. Checkout: dynamic too, and additionally the least cacheable — correctness beats latency, so no shared caching, and any client state should be recoverable if the page reloads mid-flow. The general rule is to choose per ROUTE from who the content varies by and how stale it may be, not per application.
Expert-level because the answer must vary within one app and be justified by data characteristics, not by a blanket preference for SSG or SSR.
MediumBest practice
What belongs in `layout.tsx`, and what should stay out of it?
In: the persistent shell for everything below it — chrome such as headers, navigation and footers, shared providers, and metadata that applies to the whole subtree. Out: anything specific to a single child route, and anything that must re-run on every navigation within the segment. A layout does not re-render when you navigate between its children, so state inside it survives — useful for a sidebar's scroll position, actively wrong for data that should reflect the current page.
The non-obvious half is that persistence is the point and also the hazard. Fetching in a layout gets you one fetch for many navigations, which is exactly what you want for a user menu and exactly what you do not want for something that should track the route. Layouts also cannot read the current pathname on the server, so route-dependent logic belongs in the page or in a small client component.
HardWrite code
Gate a set of routes behind auth using middleware. What can middleware safely decide?
The crucial caveat is that middleware improves UX by redirecting early — it is not the security boundary, because the data-fetching layer must authorise independently.
HardCode review
Review this Route Handler
export async function POST(req: Request) {
const { userId, amount } = await req.json();
await db.transfer(userId, amount);
return Response.json({ ok: true });
}
It trusts the client completely. `userId` comes from the request body, so any caller can move money on behalf of any account — the acting user must come from the verified session, never the payload. `amount` is unvalidated: negative, non-numeric, or absurdly large values all reach the database. There is no authentication or authorisation check at all, no input schema, no idempotency key (so a retry or double-click transfers twice), and no error handling — a thrown error becomes a 500 with whatever detail leaks out. Being a server file proves nothing about the caller; this is a public HTTP endpoint and needs to be treated as one.
The `userId`-from-body flaw is the one to lead with: it is an authorisation bypass, not an input-validation nit.
ExpertConcept
What are the real costs of the Server/Client boundary, in both directions?
Going client-ward: everything below a `"use client"` module enters the browser bundle, so a directive placed high pulls the subtree and its dependencies down with it. Props passed across the boundary must be serialisable — no functions, class instances or Dates surviving as Dates — and they are serialised into the payload, so a large object is shipped twice: once as rendered HTML and again as props for hydration. Going server-ward: Server Components cannot use state, effects or browser APIs, and cannot be imported BY a client component (only passed to one as children). The practical consequence is that you push the boundary as low as possible, pass server-rendered content through `children` rather than importing it into client code, and keep the props crossing the boundary small.
The "passed as children rather than imported" pattern is the key technique, and it is what lets a client-side interactive shell wrap server-rendered content.
MediumDebugging
"window is not defined" at build time. Where is it coming from and what are the fixes?
const width = window.innerWidth;
export default function Widget() { return <div>{width}</div>; }
Module-level code runs during server rendering, where there is no `window`. Note the position matters: the same access inside an effect would be fine, because effects only run on the client. Fixes, in order of preference: move the access into `useEffect` and hold the value in state; guard with `typeof window !== "undefined"` if you only need a one-off read; or import the component with `next/dynamic` and `ssr: false` when it genuinely cannot render on the server. Reading a viewport dimension for layout is usually better solved with CSS or a container query than with JS at all.
The useful distinction is module scope versus effect scope — candidates who reach straight for `ssr: false` are disabling server rendering to avoid a one-line fix.
MediumFollow-up
Two sibling Server Components each fetch the same user record. Does that hit your API twice?
For `fetch` in the App Router, no — identical requests within one server render pass are deduplicated automatically, so the second caller gets the first call's result. That does not extend to anything else: a direct database client or a non-`fetch` SDK call runs once per caller unless you wrap it yourself, which is what React's `cache()` is for.
This is why the App Router pattern of "let every component fetch what it needs" is not as wasteful as it sounds — it removes the prop-drilling of data through layouts without multiplying requests. The trap is assuming the deduplication is universal. A component tree where four components each call `db.user.findUnique` will issue four queries, and the fix is to wrap that function in `cache()` rather than to hoist the call.
HardMultiple choice
What does `"use client"` actually mark?
It marks an entry point into the client graph. Everything imported from there is bundled for the browser, so a stray `"use client"` high in the tree can pull most of your app into the bundle. Client Components are still server-rendered for the initial HTML, then hydrated.
The remaining 18 Next.js questions — plus mock interviews, spaced revision and progress tracking — are in the free interview prep workspace.