Next.js Interview Questions
What They Actually Ask
LinkedIn Hook
"I've interviewed for dozens of senior Next.js roles. The same 25 questions come up every single time."
Interviewers don't test whether you memorized the docs. They test whether you can defend a decision when someone pushes back. "Why SSR and not SSG?" "Why did you put that logic in middleware?" "How would you cache this endpoint?"
Most candidates fail not because they don't know Next.js — but because they answer in trivia mode. They recite definitions instead of walking through tradeoffs. They mention
revalidatewithout mentioning why. They say "use client" without explaining the cost.In Lesson 8.5 — the final lesson of this course — I've collected the 25 questions that actually get asked, grouped by theme, with concise answers that show you how to think, not just what to memorize. Plus: how to structure your answer, common debugging scenarios, and the red flags interviewers listen for.
Read the capstone lesson -> [link]
#NextJS #InterviewPrep #FrontendDevelopment #ReactJS #WebDevelopment #CareerGrowth
What You'll Learn
- The 25 most-asked Next.js interview questions with concise, correct answers
- How to structure an interview answer using Context -> Decision -> Tradeoff
- How interviewers separate "memorized docs" candidates from "senior" candidates
- Common debugging scenarios (hydration mismatch, stale cache, client leaks, env var leaks)
- The red flags interviewers listen for — even when they don't say anything
- A cheat-sheet mapping every core Next.js concept to a one-sentence answer
- Common answering mistakes that quietly sink otherwise strong candidates
The Capstone — Why This Lesson Exists
You've now walked through 32 lessons covering routing, rendering, data fetching, caching, middleware, performance, and deployment. This final lesson is different: it's not about learning a new concept — it's about packaging everything you already know into answers that land.
Interviewers rarely ask "what does getStaticProps do?" anymore. They ask: "You have a product catalog with 50,000 items that updates every few hours. How do you render it?" That's the same question in disguise, but the expected answer is a walk through the decision, not a definition.
This lesson gives you both sides: the questions as they'll actually be phrased, and the mental scaffolding to answer them like a senior engineer.
How to Structure a Next.js Interview Answer
The biggest mistake candidates make is answering in one layer — just the definition, just the API, just the pattern. Senior answers have three layers, and you should train yourself to deliver all three in under 90 seconds.
+---------------------------------------------------------------+
| THE CONTEXT -> DECISION -> TRADEOFF FRAME |
+---------------------------------------------------------------+
| |
| 1. CONTEXT (10-15 seconds) |
| "This depends on how often the data changes and who |
| sees it. Let me assume a product catalog that updates |
| hourly and is public." |
| |
| 2. DECISION (30-45 seconds) |
| "I'd use ISR with a revalidate of 3600. The build |
| generates static pages, and Next.js regenerates them |
| in the background when stale." |
| |
| 3. TRADEOFF (20-30 seconds) |
| "The tradeoff is that the first user after expiration |
| sees slightly stale data until regeneration completes. |
| If that's unacceptable, I'd switch to on-demand |
| revalidation triggered by the CMS webhook." |
| |
+---------------------------------------------------------------+
Napkin AI Visual Prompt: "Dark gradient (#0a0e1a -> #111827). Three stacked horizontal bars labeled 'Context', 'Decision', 'Tradeoff' in white monospace. The Context bar glows emerald green (#10b981), Decision bar glows purple (#8b5cf6), Tradeoff bar glows a softer blue-green. Arrows flow downward between them. Right side shows a stopwatch at 90 seconds. Title at top: 'The Senior Answer Frame'."
Why this works: interviewers are listening for judgment, not recall. Judgment is visible only when you name a tradeoff. If your answer has no tradeoff, it sounds like you copied it from a blog post. If your answer has a tradeoff, it sounds like you've shipped this to production.
The 25 Questions
Group 1: Rendering Strategies (SSR / SSG / ISR / CSR)
Q1. "What are the four rendering strategies in Next.js, and when would you pick each?"
SSG pre-renders pages at build time — pick it for marketing pages, docs, and blog posts where content rarely changes. SSR renders on every request — pick it for pages that must reflect the current user or the latest data (dashboards, personalized feeds). ISR is SSG with background regeneration — pick it for large catalogs that update periodically (e-commerce, news sites). CSR renders entirely in the browser — pick it for authenticated app shells where SEO doesn't matter and initial HTML can be a skeleton. The decision is usually driven by two axes: how fresh must the data be, and does SEO matter.
Q2. "What is the difference between SSG and ISR? Why would you ever prefer ISR?"
SSG generates every page once at build time. If you have 50,000 products and one changes, you'd need to rebuild the entire site. ISR generates pages on-demand after the first request, caches them, and regenerates them in the background based on a revalidate interval or an on-demand trigger. ISR gives you the performance of static HTML without the rebuild cost of full SSG — you pay the render cost only for pages that are actually visited. The tradeoff is that the first request after expiration serves stale data while regeneration happens behind the scenes.
Q3. "When would you avoid SSR even though it gives the freshest data?"
When the page is highly cacheable and doesn't need personalization. SSR means every request hits your origin, costs compute, and adds latency compared to static HTML served from a CDN edge. If the data can tolerate a few seconds or minutes of staleness, ISR is almost always cheaper and faster. Avoid SSR for public, SEO-indexed pages that millions of users see. Use SSR for per-user dashboards where caching would leak one user's data to another.
Q4. "How does client-side rendering fit into a Next.js app if the framework prefers server rendering?"
Client-side rendering in Next.js usually means hydrating interactive parts of a server-rendered page, not rendering the whole page in the browser. The initial HTML comes from the server (fast first paint, SEO-friendly), and then React attaches event handlers on the client. For deeply interactive sections — charts, editors, live search — you mark a component 'use client' and it runs in the browser. Full CSR is rare in App Router; it's mostly used for protected app shells behind authentication where the first paint is a loading state anyway.
Q5. "How does ISR's revalidate actually work under the hood?"
When a page has revalidate: 60, Next.js serves the cached version to every visitor for 60 seconds. After 60 seconds, the next visitor still gets the stale cached version immediately (so the response stays fast), but Next.js kicks off a background regeneration. Once that regeneration finishes, the cache is replaced and subsequent visitors get the fresh version. This is called stale-while-revalidate. On-demand revalidation via revalidatePath or revalidateTag lets you invalidate the cache instantly from a webhook or server action without waiting for the interval.
Group 2: App Router & Server Components
Q6. "What's the difference between Server Components and Client Components, and how do you choose?"
Server Components render on the server, never ship JavaScript to the browser, and can directly access databases, file systems, and secrets. Client Components render on the server for initial HTML and then hydrate in the browser so they can use useState, useEffect, event handlers, and browser APIs. The default in App Router is Server Component — you only opt in to Client by adding 'use client' at the top of the file. The rule of thumb: start with Server, drop to Client only at the leaves where interactivity is needed.
Q7. "Can a Server Component import a Client Component and vice versa?"
Server Components can import Client Components freely — the Client Component becomes a boundary, and everything inside it runs on the client. Client Components cannot directly import Server Components, but they can receive them as children or props. This is the pattern that lets you wrap a Client Component (like a theme provider) around Server Components without forcing the entire tree to become client code. Understanding this interleaving is one of the most-tested App Router concepts.
Q8. "What does adding 'use client' to a file actually do?"
It marks the file and all of its imports as a client boundary. That module and everything it imports will be bundled into the JavaScript that ships to the browser, where it will be hydrated into a React component. It does not mean the component renders only on the client — it still renders on the server for the initial HTML. The cost is bundle size: every dependency pulled into a client file travels to every user. A careless 'use client' on a file that imports a 200 KB chart library pushes that library to every visitor whether they scroll to the chart or not.
Q9. "What is the difference between layout.tsx and template.tsx in App Router?"
A layout.tsx persists across navigation — its state and DOM are preserved when you move between sibling pages, which is ideal for sidebars, headers, and anything with scroll position you want to keep. A template.tsx re-mounts on every navigation — its state resets and effects re-run, which is useful for per-page enter animations or logging page views. Most routes use layout.tsx. Reach for template.tsx only when you specifically need the fresh-mount behavior.
Q10. "What problem do Server Components solve that traditional SSR didn't?"
Traditional SSR renders React to HTML on the server, but it still ships the full component code to the client for hydration — including data-fetching libraries, markdown parsers, and other server-only dependencies that the browser never actually needs. Server Components keep that code on the server permanently. The browser receives only the HTML output and the JavaScript for interactive (client) parts. The result is smaller bundles, faster hydration, and the ability to talk to a database directly from a component without building an API layer.
Group 3: Data Fetching & Caching
Q11. "How do you fetch data in a Server Component, and what's the default caching behavior?"
You just call fetch (or a database client) directly inside an async Server Component — no getServerSideProps, no SWR, no effect. In Next.js 14, fetch results were cached by default; in Next.js 15 the default flipped to uncached. Either way, you control caching explicitly with the cache option ('force-cache' or 'no-store'), with next: { revalidate: N } for time-based, or with next: { tags: ['...'] } for tag-based on-demand invalidation. Knowing which Next version changed the default is a frequent gotcha question.
Q12. "What's the difference between revalidatePath and revalidateTag?"
revalidatePath('/products') invalidates every cached fetch associated with a specific route — useful when one route maps cleanly to the data that changed. revalidateTag('products') invalidates every cached fetch that was registered with that tag, regardless of which route it belongs to — useful when the same data appears on multiple routes and you don't want to hunt them down. Tags are more flexible and are the recommended approach for any data that's shared across the app.
Q13. "How does request deduplication work in Server Components?"
If the same component tree calls fetch('/api/user') in three different places during a single request, Next.js dedupes them into one network call thanks to React's cache function and the fetch cache layer. This means you can call your data-fetching functions wherever you need them without worrying about N+1 — a layout, a page, and a deeply nested component can all request the same resource and the server only hits it once.
Q14. "What are Server Actions and when should you use them instead of API routes?"
Server Actions are async functions marked with 'use server' that you can call directly from Client Components, typically as form actions or button handlers. Next.js turns the call into a POST under the hood, runs the function on the server, and can automatically revalidate the affected cache. Use them for form submissions, mutations, and RPC-style calls from your own UI. Use API routes (Route Handlers) when you need a public HTTP endpoint that external services, webhooks, or non-React clients will call.
Q15. "How does the Next.js Data Cache differ from the Full Route Cache?"
The Data Cache stores the results of individual fetch calls (and similar data calls) and lives between requests — it's what revalidate and tags control. The Full Route Cache stores the fully-rendered HTML and React Server Component payload of an entire route at build time, so visitors get static HTML with no rendering at all. They work together: at build, Next.js fetches data (Data Cache) and renders the route (Full Route Cache). At runtime, invalidating data automatically invalidates the dependent rendered pages.
Group 4: Routing & Middleware
Q16. "What are parallel routes and intercepting routes, and what problems do they solve?"
Parallel routes let you render multiple independent pages in the same layout simultaneously using named slots (@modal, @sidebar). They're how you build dashboards where different panels have their own loading and error states. Intercepting routes let you display a route in a different context depending on how the user arrived — clicking a photo in a feed opens it as a modal, but refreshing the URL shows the full page. Together they're how Next.js replicates Instagram-style photo modals without hacking state into a global store.
Q17. "What can middleware do, and what should it not do?"
Middleware runs on the edge before a request reaches your route handlers or pages. Use it for authentication redirects, A/B test assignment, feature flags, geo-based routing, locale detection, and request rewrites. Don't use it for heavy computation, database queries, or anything that needs Node.js APIs — it runs on the Edge Runtime, which has strict limits and no filesystem access. Middleware must return fast (tens of milliseconds) because it runs on every matching request.
Q18. "How would you protect a group of routes behind authentication?"
The cleanest pattern is middleware with a matcher config that targets the protected routes (e.g. /dashboard/:path*), reads the session cookie, and redirects unauthenticated requests to the login page. For finer-grained checks (role-based permissions, resource ownership), layer a server-side check inside the layout or page itself — middleware handles the coarse "is anyone logged in," while the layout handles "is this user allowed to see this resource."
Q19. "What's the difference between a redirect, a rewrite, and a middleware rewrite?"
A redirect (via redirects config or NextResponse.redirect) sends the browser a 3xx status and changes the URL in the address bar — users see /old-path becoming /new-path. A rewrite (via rewrites config or NextResponse.rewrite) proxies the request internally — the browser keeps seeing /old-path but gets the content of /new-path. Middleware rewrites are the dynamic version: the destination is computed per-request based on cookies, headers, geo, or feature flags, which is how you build A/B tests and per-region variants without duplicating routes.
Group 5: Performance & Deployment
Q20. "What are Core Web Vitals and how does Next.js help improve them?"
Core Web Vitals are Google's three user-experience metrics: LCP (Largest Contentful Paint, load speed), INP (Interaction to Next Paint, responsiveness, which replaced FID), and CLS (Cumulative Layout Shift, visual stability). Next.js improves LCP through automatic image optimization (next/image), font preloading (next/font), and server rendering. It improves INP by shipping less JavaScript via Server Components and automatic code splitting. It improves CLS through next/image dimension reservation and next/font size-adjust fallbacks. These aren't just perf wins — they're ranking signals in Google Search.
Q21. "How does Next.js handle code splitting, and what can you do to control it?"
Next.js automatically splits JavaScript per route, so visiting /about doesn't download the code for /checkout. Shared dependencies end up in common chunks that are cached across routes. For finer control, use next/dynamic to lazily load heavy components (charts, editors, modals) so they only download when rendered. Pair next/dynamic with { ssr: false } for components that can't run on the server (e.g. libraries that touch window), and with a loading fallback to keep CLS stable.
Q22. "What's the difference between deploying a Next.js app to Vercel versus a Node.js server?"
On Vercel, each route is mapped to the right infrastructure automatically: static pages go to the edge CDN, ISR pages use the on-demand revalidation layer, Server Components run as serverless functions, and middleware runs at the edge. On a generic Node server (or a container), you run next start, which serves everything from a single long-lived process — you lose automatic edge distribution and you need to configure your own CDN, caching headers, and ISR persistence. Self-hosting is possible and well-documented but requires more operational work.
Q23. "How do you handle environment variables in Next.js, and what's the risk with NEXT_PUBLIC_?"
Variables defined in .env.local are available on the server by default. To expose a variable to the browser, you must prefix it with NEXT_PUBLIC_ — that prefix is a signal that the value will be inlined into the client bundle at build time and visible to anyone who views the page source. The risk is accidentally prefixing a secret (API key, service token) with NEXT_PUBLIC_ and shipping it to every user. Never put secrets in NEXT_PUBLIC_ variables. Always audit which env vars cross the server-client boundary.
Group 6: Architecture / "How Would You..."
Q24. "How would you design a Next.js e-commerce site with 100,000 products that updates hourly?"
Start with the context: public pages, SEO-critical, data moderately fresh. I'd use ISR with tag-based revalidation — product pages rendered on first visit, cached, and invalidated by webhook when the product updates in the CMS. Category listings get a short revalidate (300-600 seconds) because they change more often. The cart and checkout are Client Components with Server Actions for mutations — they need per-user state and never want caching. I'd lean on next/image for product photos, parallel routes for the product-plus-reviews layout, and middleware for geo-based currency and locale. The tradeoff is complexity: on-demand revalidation requires wiring webhooks, which adds operational surface area, but the payoff is static-fast pages with near-real-time updates.
Q25. "How would you add authentication to a Next.js App Router project from scratch?"
I'd use an auth library that supports App Router (Auth.js / NextAuth, Clerk, or Lucia) rather than rolling my own. The session token lives in an HTTP-only cookie so Server Components can read it directly via cookies() from next/headers. Middleware enforces the coarse check on protected route groups and redirects unauthenticated users to /login. Protected layouts perform the fine-grained check — fetching the user and passing it down as a prop or via React context for Client Components. Mutations use Server Actions that revalidate the session's cached data. The tradeoff between the libraries is control versus convenience: Clerk is fastest to ship but locks you in; Auth.js is flexible but you own more configuration.
Common Debugging Scenarios
Interviewers love asking "have you ever had to debug X?" because it reveals whether you've actually shipped something. Here are the four scenarios that come up most.
Scenario 1: Hydration Mismatch
Symptom: Console error "Text content did not match" or "Hydration failed because the initial UI does not match what was rendered on the server."
Root cause: The server rendered HTML one way and the client rendered it another way on hydration. Common triggers: using Date.now(), Math.random(), window.innerWidth, localStorage, or typeof window !== 'undefined' directly in render; rendering different content based on user-agent that the server doesn't know about; or a browser extension mutating the DOM before React hydrates.
Fix: Move time/randomness/client-only data into a useEffect so it only runs after hydration, or wrap the component in next/dynamic with { ssr: false }. For known mismatches like a theme switcher, use suppressHydrationWarning on the specific element only.
Scenario 2: Stale Cache After a Mutation
Symptom: User submits a form or clicks "update," the backend definitely saved the change, but the page still shows the old data until a hard refresh.
Root cause: The Data Cache and Full Route Cache are still serving the old version because nothing told them to invalidate.
Fix: After the mutation (usually in a Server Action), call revalidatePath('/path') or revalidateTag('tag'). Make sure the original fetch was registered with the same tag. If the page is a Client Component reading from state, also call router.refresh() to re-fetch the Server Component tree without a full page reload.
Scenario 3: 'use client' Leaks
Symptom: Bundle size balloons unexpectedly. A heavy library like date-fns, lodash, or a charting package shows up in the client bundle even though only one page uses it.
Root cause: A Client Component imported a utility file, and that utility file re-exported something from the heavy library. Because the utility is imported from a client file, everything it touches becomes part of the client bundle transitively.
Fix: Audit your client files. Extract pure data transformations into Server Components or into server-only utilities (use the server-only package to enforce the boundary). Push 'use client' as far down the tree as possible — it should sit on the leaf component that actually uses useState, not on the top of the page.
Scenario 4: Environment Variable Leak
Symptom: A secret API key appears in the browser's Network tab or in View Source.
Root cause: The variable was defined as NEXT_PUBLIC_SECRET_KEY instead of SECRET_KEY. The NEXT_PUBLIC_ prefix inlines the value into the client bundle at build time. Once shipped, the key is public forever — rotating it is the only remediation.
Fix: Remove the prefix, rotate the key, and move the usage to a Server Component, Server Action, or Route Handler where the server-side variable is accessible. Add a build-time check (or a lint rule) that fails CI if NEXT_PUBLIC_ is used with any name containing SECRET, KEY, or TOKEN.
+---------------------------------------------------------------+
| DEBUGGING QUICK LOOKUP |
+---------------------------------------------------------------+
| |
| Hydration mismatch -> move client-only code to useEffect |
| Stale cache -> revalidateTag / revalidatePath |
| Bundle bloat -> push 'use client' down the tree |
| Leaked env var -> rotate key, drop NEXT_PUBLIC_ prefix |
| Infinite redirect -> check middleware matcher config |
| 404 on dynamic route -> verify generateStaticParams output |
| |
+---------------------------------------------------------------+
Red Flags Interviewers Watch For
These are the patterns that quietly sink otherwise strong candidates. Interviewers rarely call them out in the room — they just stop asking deeper questions.
1. Confusing Pages Router and App Router concepts.
Saying getServerSideProps in an App Router question signals you haven't actually worked with App Router. Know which era each API belongs to, and when asked about App Router, stay in App Router vocabulary (Server Components, Route Handlers, Server Actions).
2. Treating 'use client' as free.
Candidates who sprinkle 'use client' without acknowledging bundle cost come across as junior. Every client boundary has a size and hydration cost. Senior answers mention that cost unprompted.
3. No mention of caching when discussing data fetching. If you describe a data flow without touching on caching, revalidation, or staleness, interviewers assume you've only built toy apps. Real production apps live or die on the cache layer.
4. "I'd just use SSR for everything." This is the single most common red flag. It signals you're avoiding the decision rather than making it. SSR has real cost — compute, latency, cache miss — and defaulting to it means you haven't thought about the user or the bill.
5. Not knowing the difference between Edge and Node runtimes.
Middleware and some Route Handlers run on the Edge Runtime, which has no fs, no native Node APIs, and tight size limits. Confusing the two leads to real production incidents, and interviewers probe for it.
6. Overconfidence without tradeoffs. Answering "ISR, definitely" without naming what could go wrong reads as inexperience. Every architectural choice has a downside — state it before the interviewer has to ask.
7. Copy-pasting blog-post phrases. "Hybrid rendering," "best of both worlds," "lightning fast" — these are marketing phrases. Senior engineers describe systems in concrete terms (requests, milliseconds, cache hits, bytes).
Common Mistakes (In Answering)
1. Answering the definition instead of the question. When asked "how would you render a product catalog," candidates often define SSG, SSR, and ISR one after another. That's a textbook chapter, not an answer. Pick one, say why, name the tradeoff, and stop. The interviewer will ask for the alternatives if they want them.
2. Leading with the API instead of the intent.
Starting with "I'd use revalidateTag..." tells the interviewer what but not why. Lead with the decision ("I'd cache this and invalidate on write"), then name the API. API-first answers sound like you're reciting.
3. Skipping the tradeoff. Every senior answer names a cost. If you can't think of a tradeoff, you haven't thought hard enough about the choice — and the interviewer will notice.
4. Rambling past the 90-second mark. The longer you talk without a pause, the more likely you are to say something wrong. Deliver your Context -> Decision -> Tradeoff, then stop and let the interviewer steer. Silence after a clean answer is confidence; filling silence is panic.
5. Refusing to say "I don't know." If you've never used parallel routes, say so, then reason from first principles about what they might do. Fabricating an answer is a hard fail; reasoning out loud is usually a pass.
6. Ignoring the "how would you" framing. "How would you..." questions are architecture questions in disguise. The interviewer wants to hear you pick a stack, name constraints, list components, and walk the request path. Don't answer them with a single API name.
7. Not asking clarifying questions on ambiguous prompts. "How would you build a dashboard" has a dozen valid answers depending on scale, auth, and freshness. Asking "is this per-user, and how fresh does the data need to be?" shows senior judgment before you've even answered.
Quick Reference — Concept to One-Sentence Answer
+---------------------------------------------------------------+
| NEXT.JS CONCEPT CHEAT SHEET |
+---------------------------------------------------------------+
| |
| CONCEPT -> ONE-SENTENCE ANSWER |
| ------------------- ---------------------------------- |
| SSG -> Pre-render at build, serve static. |
| SSR -> Render on every request, freshest. |
| ISR -> Static, regenerated in background. |
| CSR -> Render in browser after hydration. |
| Server Component -> Runs only on server, zero JS ship. |
| Client Component -> Hydrates in browser, has state. |
| 'use client' -> Marks a module as client boundary. |
| Server Action -> RPC from client to server function. |
| Route Handler -> HTTP endpoint at app/api/.../route. |
| Middleware -> Edge code running before routing. |
| Layout -> Persistent shell across children. |
| Template -> Re-mounts on every navigation. |
| Parallel Route -> Named slot rendered in a layout. |
| Intercepting Route -> Context-dependent route rendering. |
| revalidate: N -> Refresh cache every N seconds. |
| revalidateTag -> Invalidate all fetches with a tag. |
| revalidatePath -> Invalidate a specific route cache. |
| Data Cache -> Stores fetch results between reqs. |
| Full Route Cache -> Stores rendered HTML of a route. |
| next/image -> Auto-optimized, CLS-safe images. |
| next/font -> Self-hosted, zero-CLS fonts. |
| next/dynamic -> Lazy-load a component on demand. |
| NEXT_PUBLIC_ -> Exposes env var to client bundle. |
| Edge Runtime -> Fast, limited subset of Node APIs. |
| Node Runtime -> Full Node.js, slower cold start. |
| |
+---------------------------------------------------------------+
| Question Theme | Winning Answer Pattern |
|---|---|
| "Which rendering strategy?" | State freshness need -> pick -> name tradeoff |
| "Server or Client Component?" | Default Server, drop to Client at interactivity leaves |
| "How do you cache this?" | Tag the fetch, revalidate on write, name the staleness window |
| "How do you protect routes?" | Middleware for coarse check, layout for fine-grained |
| "Why is the bundle so big?" | 'use client' pushed too high, heavy imports leaking |
| "How would you deploy this?" | Pick host, name runtime per route, mention cache layer |
| "How do you debug hydration?" | Identify nondeterministic render, move to effect |
| "How do you mutate data?" | Server Action + revalidateTag, not a fetch from client |
Prev: Lesson 8.4 -- Monitoring and Error Tracking Next: You've completed the course! Back to Course Roadmap
This is Lesson 8.5 of the Next.js Interview Prep Course -- 8 chapters, 33 lessons.
Congrats on finishing the course. You started at "what is a route" and ended at "here's how I'd architect a 100,000-product catalog with tag-based revalidation." That's the full arc from syntax to judgment — and judgment is what the interview is actually testing. Go get the offer.