App Router • Server Components • Rendering & Caching • Server Actions • Deployment • 2026

Next.js Interview Questions

30 questions What each one tests, an answer frame, a spoken answer 35 min read

This page is for developers facing a Next.js round, from a first frontend job to a senior full-stack role. Most interviews open with the App Router and how it differs from the Pages Router, then move to server and client components, rendering modes, and the caching rules that trip people up in production. Stronger rounds add server actions, middleware, SEO, performance and self-hosting, plus a story from your own work and a judgement call. Each question shows what the interviewer is really checking, the shape of a strong answer and a short answer you can say out loud.

Search all questions by round, difficulty and level, or save the ones you want to practise.

Routing & Layouts 6 questions

Easy Technical round Fresher, Mid-level Practice question

1. What's the difference between the App Router and the Pages Router, and which would you choose for a new project?

What the interviewer is really testing:
Whether you understand the model shift behind the App Router, not just that the folder name changed.
Answer frame:

Pages Router: files in pages/ become routes; data comes from getServerSideProps or getStaticProps; every component ships to the browser.

App Router: folders in app/ define routes; components are Server Components by default and can await data themselves.

Extras: nested layouts that keep state, per-segment loading and error files, and streaming.

Choice: new projects on the App Router; both routers can live in one app while migrating.

Sample spoken answer:

"In the Pages Router, each file under pages is a route, and data loading happens in special functions like getServerSideProps or getStaticProps that run before the page renders and pass props in. Every component on the page is also sent to the browser as JavaScript. The App Router, under the app folder, is built on React Server Components. Components run on the server by default, so I can make the component itself async and await my data right there, and only the parts I mark with 'use client' ship JavaScript. It also gives me nested layouts that keep their state between navigations, loading and error files per segment, and streaming. For a new project I'd pick the App Router, since that's where new features land. The Pages Router is still supported, and both can live in one app, which matters when migrating."

Red flag to avoid:

Describing the App Router as just a new folder name, without mentioning Server Components or how data loading changed.

They may ask next:
  • Can the same URL be handled by both routers at once?
  • Where does the logic from getServerSideProps go in the App Router?
Say it in 60 seconds
Easy Technical round Fresher, Mid-level Practice question

2. In the App Router, what do the page, layout, loading, error and not-found files each do?

What the interviewer is really testing:
Whether you know the building blocks of a route segment and how they nest, which every App Router task relies on.
Answer frame:

page and layout: page makes the segment reachable; layout wraps it and its children and stays mounted across navigation.

loading and error: loading becomes a Suspense fallback; error becomes an error boundary and must be a Client Component.

not-found: shown when notFound() is called or no route matches.

Sample spoken answer:

"A folder only becomes a public URL when it has a page file. The layout wraps that page and everything nested below it, and the nice part is that layouts don't re-render or lose state when you move between sibling pages, so a sidebar or a player keeps going. The loading file is shown instantly while the segment's content loads, because Next wraps the page in a Suspense boundary with it as the fallback. The error file is an error boundary for that segment, so one broken section shows a fallback with a retry button instead of taking down the whole app, and it has to be a Client Component. The not-found file renders when I call notFound(), say when a product ID doesn't exist, or when nothing matches the URL. The root layout is the one that's required, and it has to render the html and body tags."

Red flag to avoid:

Thinking every folder under app is automatically a route, or not knowing that a layout keeps its state between navigations.

They may ask next:
  • Why doesn't an error file catch errors thrown in the layout of the same segment?
  • When would you use a template file instead of a layout?
Say it in 60 seconds
Easy Technical round Fresher, Mid-level Practice question

3. How do dynamic route segments work? Explain [id], [...slug] and [[...slug]] with an example.

What the interviewer is really testing:
Whether you can map URLs to folders and read the parameters correctly inside a page.
Answer frame:

[id]: matches exactly one segment, like /products/42, and arrives as params.id.

[...slug]: catch-all; matches one or more segments and gives an array.

[[...slug]]: optional catch-all; also matches the bare path with no extra segments.

Sample spoken answer:

"Square brackets in a folder name make that segment dynamic. So app/products/[id]/page.tsx matches /products/42, and the page receives params with id equal to the string '42'. A catch-all, [...slug], matches one or more segments: for a docs site, /docs/setup/install gives me slug as an array of setup and install. But it won't match plain /docs. The optional catch-all, with double brackets, matches that bare /docs as well, and slug is just undefined there. In recent versions params is a promise, so in the page I await it before reading the values. And params are always strings, so if I need a number I convert and validate it, and I call notFound() if the record doesn't exist."

Code:
// app/blog/[slug]/page.tsx
import { notFound } from 'next/navigation';

export default async function Page(
  { params }: { params: Promise<{ slug: string }> }
) {
  const { slug } = await params;
  const post = await getPost(slug);
  if (!post) notFound();
  return <h1>{post.title}</h1>;
}
Red flag to avoid:

Forgetting that params values are strings, or assuming a catch-all segment also matches the parent path.

They may ask next:
  • How do you read query string values like ?page=2 in a server page?
  • What happens if two sibling dynamic folders at the same level use different names?
Say it in 60 seconds
Medium Technical round Mid-level Practice question

4. What does generateStaticParams do, and what happens when someone visits a slug you didn't build ahead of time?

What the interviewer is really testing:
Whether you can control which dynamic pages are prerendered at build time and what the fallback behaviour is.
Answer frame:

Purpose: returns the params to prerender at build, the App Router's version of getStaticPaths.

Unknown slugs: by default rendered on demand at the first request, then cached.

Lock down: export dynamicParams = false to return a 404 for anything not in the list.

Sample spoken answer:

"generateStaticParams runs at build time and returns an array of params objects, say one per blog post slug. Next then prerenders each of those pages as static HTML. It replaces getStaticPaths from the Pages Router. For a slug that wasn't in the list, the default is that Next renders it on demand the first time someone asks for it, and after that it can be served from cache like the others. That's useful for a huge catalogue: I prebuild the most visited products to keep builds fast and let the long tail fill in on demand. If the list is complete and anything else should be a 404, I export dynamicParams set to false from the route. One thing to watch: if the page reads something request-specific like cookies, it's rendered dynamically anyway, so the static list doesn't help."

Red flag to avoid:

Saying unknown slugs always return a 404, or prebuilding every page and letting the build crawl.

They may ask next:
  • How would you keep build times down for a site with a very large catalogue?
  • How do you refresh one of those prebuilt pages when its content changes?
Say it in 60 seconds
Hard Technical round Mid-level, Senior Practice question

5. What are route groups, parallel routes and intercepting routes? Describe a case where you'd use each.

What the interviewer is really testing:
Whether you can structure a complex app, like a dashboard or a modal with its own URL, using the App Router's advanced routing.
Answer frame:

Route groups: a folder in parentheses organises routes or gives them their own layout without changing the URL.

Parallel routes: @slot folders render several pages side by side in one layout, each with its own loading and error states.

Intercepting routes: (.) folders show a route inside the current page, like a modal, while a direct visit shows the full page.

Sample spoken answer:

"Route groups are folders in parentheses, like (marketing) and (app). They don't appear in the URL, so I use them to give the marketing pages one layout and the logged-in area another. Parallel routes are named slots, folders starting with @, like @analytics and @team. The parent layout receives each slot as a prop and renders them together, and each can load or fail on its own. I also add a default file so a slot knows what to show when the URL doesn't match it. Intercepting routes let me catch a navigation and show it in context. The classic case is a photo feed: clicking a photo opens it in a modal with the URL /photo/5, but if you refresh or share that link, you get the full photo page. A parallel @modal slot plus an intercepting route is how I build shareable modals."

Red flag to avoid:

Thinking route groups change the URL, or building a URL-driven modal with client state only so the link can't be shared.

They may ask next:
  • What does the default file do in a parallel route, and what happens without it?
  • Why does a hard refresh show the full page instead of the modal?
Say it in 60 seconds
Hard Situational round Senior Practice question

6. Your team wants to move a large Pages Router app to the App Router this quarter while still shipping features. How would you plan it?

What the interviewer is really testing:
Whether you can plan an incremental, low-risk migration instead of a big-bang rewrite that stalls the roadmap.
Answer frame:

Incremental: both routers run in one app, so move route by route, starting with simple, low-traffic pages.

Foundations first: root layout, shared providers, and auth and data helpers that work in both worlds.

Guardrails: compare performance and errors before and after each route, and keep a rollback path.

Sample spoken answer:

"I'd push back on a big rewrite and plan it route by route, because both routers can live in the same app. First, I'd upgrade Next and React and fix anything that breaks, as its own release. Then I'd build the app folder foundations: a root layout, a Providers component for our existing contexts, and data functions that both routers can call, so we aren't writing everything twice. Then I'd move pages in order of risk, starting with simple static pages like about or docs, then content pages, and last the complex, highly interactive ones like checkout. Each move is its own small PR: replace getServerSideProps with data fetched in the server component, split out the client parts, and compare metrics and errors before and after. Feature work carries on in whichever router the page currently lives in, and a route only ever exists in one router."

Red flag to avoid:

Proposing to freeze features and rewrite the whole app on the App Router in one go.

They may ask next:
  • Which pages would you leave on the Pages Router longest, and why?
  • What happens when a user navigates between a page in pages/ and one in app/?
Say it in 60 seconds

Server Components 4 questions

Easy Technical round Fresher, Mid-level Practice question

7. What is a Server Component, and what makes you add 'use client' to a file?

What the interviewer is really testing:
Whether you know the default in the App Router and the concrete reasons a component has to run in the browser.
Answer frame:

Server default: components in app/ run on the server, can be async, can read data directly and send no JavaScript for themselves.

Client triggers: state, effects, event handlers, browser APIs like window or localStorage, and libraries that need them.

Placement: put 'use client' as low in the tree as possible, on the small interactive pieces.

Sample spoken answer:

"In the App Router, every component is a Server Component unless I say otherwise. It renders on the server, it can be an async function that queries the database or calls an API directly, and its own code never ships to the browser, which keeps the bundle small and keeps secrets on the server. I add 'use client' at the top of a file when the component needs interactivity: useState or useEffect, an onClick handler, browser-only things like window or localStorage, or a library that uses those. A Client Component is still rendered to HTML on the server for the first load; it just also hydrates in the browser. My rule is to keep 'use client' at the leaves: the product page stays on the server and only the add-to-cart button becomes a Client Component."

Red flag to avoid:

Saying Client Components only ever render in the browser, or putting 'use client' on whole pages out of habit.

They may ask next:
  • Can a Server Component use useState or useContext?
  • Is a Client Component rendered on the server at all?
Say it in 60 seconds
Medium Technical round Mid-level Practice question

8. Once a file has 'use client', what happens to everything it imports? How do you still render a Server Component inside a Client Component?

What the interviewer is really testing:
Whether you understand that 'use client' marks a boundary in the module graph, which is where most accidental bundle growth comes from.
Answer frame:

Boundary: the directive marks an entry point; every module that file imports becomes client code too.

No direct import: a Client Component can't import a Server Component and keep it on the server.

Composition: pass the Server Component in as children or another prop from a server parent.

Sample spoken answer:

"'use client' isn't a label on one component, it's a boundary. Everything that file imports gets pulled into the client bundle as well, so if a client component imports a big markdown renderer, that renderer ships to the browser even if it didn't need to. It also means I can't import a Server Component from a client file and have it stay on the server. The fix is composition. Say I have a client Tabs component that tracks which tab is open. In a server page, I render Tabs and pass the server-rendered panels to it as children. The server renders those panels first, and Tabs just receives the finished result as a slot. It can show or hide them, but their code never goes to the browser. Same idea for a client context provider wrapping server content in the root layout."

Red flag to avoid:

Believing 'use client' affects only the one component, so the bundle quietly grows with everything it imports.

They may ask next:
  • What kinds of props can you pass from a Server Component to a Client Component?
  • Do you need 'use client' in every file of a client subtree?
Say it in 60 seconds
Medium Technical round Fresher, Mid-level Practice question

9. You see a hydration mismatch error in a Next.js app. What causes it, and how do you fix it without just hiding it?

What the interviewer is really testing:
Whether you understand that the server HTML and the first client render must match, and can find the real source of the difference.
Answer frame:

Cause: the first render in the browser produced different output from the HTML the server sent.

Common sources: dates, random values, reading window or localStorage during render, locale or time zone formatting, invalid HTML nesting, browser extensions.

Fixes: make the first render deterministic, move browser-only values into an effect, or load that piece on the client only.

Sample spoken answer:

"A hydration error means React took the HTML the server sent, rendered the same component in the browser, and got something different. The usual suspects are rendering Date.now or Math.random, checking typeof window or reading localStorage during render, formatting dates where the server and browser time zone or locale differ, and invalid HTML, like a div inside a p tag, which the browser quietly rearranges. Browser extensions that inject markup can cause it too. My fix depends on the cause. For browser-only values, I render a stable placeholder first and set the real value in useEffect. For a widget that can't work on the server at all, I load it with next/dynamic and ssr set to false from a Client Component. For a timestamp that's expected to differ, suppressHydrationWarning on that one element is fine, but I don't sprinkle it around."

Red flag to avoid:

Turning off server rendering for the whole page, or adding suppressHydrationWarning everywhere, just to make the error go away.

They may ask next:
  • Why might the error only appear for users in certain time zones?
  • What's the cost of rendering a component only on the client?
Say it in 60 seconds
Medium Situational round Fresher, Mid-level Practice question

10. A teammate adds 'use client' to the root layout because a theme provider threw an error there. What do you tell them?

What the interviewer is really testing:
Whether you can fix a context provider problem without turning the app shell into client code, and explain it kindly.
Answer frame:

Why not: the layout becomes client code, so its imports ship to the browser and it can't be async or export metadata.

Fix: a small Providers client component that wraps children, rendered by the server layout.

Tone: explain the why, pair on the change, and write the pattern down so it doesn't come back.

Sample spoken answer:

"I'd thank them for unblocking it, then explain why I'd do it differently. The error happens because context providers use React state, so they need to be client code. But putting 'use client' on the root layout makes the layout itself client code: everything it imports ships to the browser, it can't be async to fetch data, and it can no longer export metadata, which would quietly break our page titles. The standard fix is a tiny Providers file with 'use client' that wraps its children in the theme provider. The root layout stays a Server Component and just renders Providers around children, and the pages passed through as children stay Server Components. I'd pair with them for ten minutes to make that change, show that the metadata comes back, and suggest we add the pattern to our contributing guide."

Red flag to avoid:

Accepting the change because it works, without noticing the lost metadata and the larger bundle.

They may ask next:
  • Why can a Client Component render Server Components that are passed in as children?
  • How would you catch this kind of change in code review?
Say it in 60 seconds

Rendering 3 questions

Easy Technical round Fresher, Mid-level Practice question

11. Explain SSR, SSG and ISR. How do you decide which one a page should use?

What the interviewer is really testing:
Whether you can match a rendering strategy to how often the data changes and whether it's personal to the user.
Answer frame:

SSG: HTML built at build time; fastest, but only as fresh as the last build.

SSR: HTML built on every request; always fresh and can be personal, but costs server time per visit.

ISR: static pages that regenerate in the background after a time window or on demand.

Decision: personal or real-time data means SSR; shared content that changes sometimes means ISR; rarely changing means SSG.

Sample spoken answer:

"With static generation, the HTML is built at build time and served from a CDN, so it's as fast as it gets, but it only changes when I rebuild. Server-side rendering builds the HTML on every request, so it's always fresh and can depend on the user, like reading their session cookie, but every visit costs server work. ISR sits in between: the page is static, but I set a revalidate time, and after that window the next request still gets the cached page while Next regenerates it in the background, so later visitors get the new version. I can also trigger it on demand when content changes. So a marketing page is static, a product page is ISR, and a dashboard with the user's own orders is SSR. In the App Router these come from how the route fetches and caches its data, not separate APIs."

Red flag to avoid:

Choosing SSR for everything to be safe, or thinking ISR makes the first visitor after the window wait for the rebuild.

They may ask next:
  • What does a visitor see if the background regeneration fails?
  • How would you render a page that's mostly static but shows the signed-in user's name?
Say it in 60 seconds
Hard Technical round Mid-level, Senior Practice question

12. How does Next.js decide whether a route is rendered statically or dynamically, and what can quietly make a page dynamic?

What the interviewer is really testing:
Whether you can predict and control rendering, so a page you thought was static isn't hitting the server on every request.
Answer frame:

Default: a route is prerendered at build unless something in it needs the incoming request.

Triggers: cookies(), headers(), searchParams, fetches with cache: 'no-store', or segment config like dynamic = 'force-dynamic'.

Check: read the build output, which marks each route static or dynamic, and trace the import that reads request data.

Sample spoken answer:

"At build time Next tries to prerender each route. If, while rendering, it hits something that can only be known per request, the route becomes dynamic. The obvious triggers are reading cookies or headers, using searchParams, opting a fetch out of caching with no-store, or setting the route's dynamic config to force-dynamic. The quiet ones are the problem. An auth helper in the root layout that reads the session cookie makes every page under it dynamic. So when a team says their blog got slow, I check the build output, which labels each route static or dynamic, then trace what's reading request data. The fix is usually to move that read out of the shared layout, for example loading the user's name on the client after the page arrives, so the rest stays static. With partial prerendering, a Suspense boundary around the dynamic piece lets the static shell still be prebuilt."

Red flag to avoid:

Thinking static or dynamic is chosen once for the whole project, or never checking the build output to see what really happened.

They may ask next:
  • What's the difference between a dynamic route segment and a dynamically rendered route?
  • How would you make the build fail if a route you expect to be static turns dynamic?
Say it in 60 seconds
Medium Technical round Mid-level Practice question

13. What is streaming in Next.js, and how do loading.tsx and Suspense boundaries help a page with one slow section?

What the interviewer is really testing:
Whether you can keep a page responsive when one data source is slow, instead of blocking everything on it.
Answer frame:

Streaming: the server sends HTML in chunks as each part is ready instead of waiting for the whole page.

loading file: wraps the segment's page in a Suspense fallback, shown instantly on navigation.

Suspense: wrap just the slow component so the fast parts render first and it fills in later.

Sample spoken answer:

"Without streaming, a server-rendered page waits for its slowest query before sending anything. With streaming, the server sends the shell and whatever is ready immediately, then sends the rest as each part finishes, over the same response. In the App Router I get that two ways. A loading file in a segment becomes the Suspense fallback for that page, so on navigation the user sees a skeleton straight away instead of a frozen screen. For finer control, I wrap just the slow piece in Suspense. On a product page, the details come from a fast query and render right away, while the reviews component, which calls a slow service, sits in its own Suspense boundary with a small skeleton and streams in when it's done. The key is that the slow component fetches its own data, so the page itself isn't awaiting it."

Red flag to avoid:

Awaiting every query at the top of the page and then wrapping it in Suspense, which streams nothing.

They may ask next:
  • Does streaming hurt SEO, since some content arrives later?
  • What status code does the page send if something fails after streaming has started?
Say it in 60 seconds

Data & Caching 4 questions

Hard Technical round Mid-level, Senior Practice question

14. Next.js caches in several places. Walk me through those caching layers and how you'd opt out of each one.

What the interviewer is really testing:
Whether you can explain stale or duplicated data by naming the right cache, instead of guessing and adding no-store everywhere.
Answer frame:

Request memoization: identical fetch calls in one render pass run once.

Data Cache: fetch results kept on the server across requests until revalidated.

Full Route Cache: the rendered output of static routes, made at build time or on revalidation.

Router Cache: the browser keeps visited route payloads in memory for fast back and forward navigation.

Sample spoken answer:

"I think of four layers. Request memoization is per render: if the layout and the page fetch the same URL, it runs once, and for database calls I get the same effect by wrapping the function in React's cache. The Data Cache is on the server and outlives the request: a fetch result can be stored and reused until its revalidate time, or until I call revalidateTag or revalidatePath. The Full Route Cache stores the rendered output of static routes, so no code runs at request time at all. And the Router Cache lives in the browser, holding route payloads so back and forward feel instant. To opt out, I set cache to no-store on the fetch, make the route dynamic so it skips the route cache, and call router.refresh on the client. The defaults changed between major versions, fetch stopped being cached by default, so I always check which version a project runs."

Red flag to avoid:

Blaming 'the cache' without knowing which one, or fixing staleness by making every route dynamic.

They may ask next:
  • If revalidatePath runs inside a Server Action, does the user who made the change see fresh data straight away?
  • How do you cache a database query that doesn't go through fetch?
Say it in 60 seconds
Medium Technical round Mid-level Practice question

15. An admin edits a product, but the product page keeps showing the old details. How do you make the page update properly?

What the interviewer is really testing:
Whether you know on-demand revalidation and tie cache invalidation to the write, instead of shortening every cache timer.
Answer frame:

Diagnose: the page or its fetch is cached, so the write never reaches it.

Invalidate on write: call revalidatePath or revalidateTag in the Server Action or Route Handler that saves the change.

Tag fetches: tag the product fetch so every page that shows that product refreshes together.

Sample spoken answer:

"The page is being served from cache, either the Data Cache holding the old fetch result or the prerendered route. Lowering revalidate to a few seconds would hide it but waste work on every product. The right fix is on-demand revalidation at the moment of the write. In the Server Action that saves the product, after the database update succeeds, I call revalidatePath for that product's URL. Better still, I tag the product fetch with something like product-42 and revalidate that tag, because the same product also shows up on category pages and search results, and a tag refreshes all of them at once. If the edit happens in a separate CMS, I expose a Route Handler as a webhook, check a shared secret, and revalidate there. Then I test it on a production build, since dev mode doesn't cache the same way."

Code:
'use server';
import { revalidatePath } from 'next/cache';

export async function saveProduct(id: string, formData: FormData) {
  // check the caller is an admin before writing
  await db.product.update({
    where: { id },
    data: { name: String(formData.get('name')) },
  });
  revalidatePath(`/products/${id}`);
}
Red flag to avoid:

Setting every page to no-store or a tiny revalidate time instead of invalidating when the data actually changes.

They may ask next:
  • How would a CMS webhook trigger the same refresh securely?
  • Why might this work in development but fail after deploy?
Say it in 60 seconds
Medium Coding round Mid-level Practice question

16. A server page awaits three independent API calls one after another and takes three seconds to load. How do you fix it?

What the interviewer is really testing:
Whether you spot request waterfalls in server code and know both the parallel fix and the streaming fix.
Answer frame:

Spot it: each await blocks the next even though none needs the others' results.

Parallel: start all three together and await them with Promise.all.

Stream: if one call is much slower, move it into its own component behind Suspense.

Sample spoken answer:

"Each await pauses the function, so three one-second calls take three seconds even though none depends on the others. The first fix is to start them together and await Promise.all, so the page waits only as long as the slowest call. If one call is much slower than the rest, say recommendations, I go further: I move it into its own async component, wrap it in Suspense with a skeleton, and let it stream in, so the main content isn't blocked at all. I also watch for waterfalls across components, where a parent awaits its data before rendering a child that only then starts its own fetch. That's the same problem spread over files. And if one failed call shouldn't break the whole page, I use Promise.allSettled and render a fallback for the part that failed."

Code:
export default async function Dashboard() {
  const [user, orders, alerts] = await Promise.all([
    getUser(),
    getOrders(),
    getAlerts(),
  ]);
  return <DashboardView user={user} orders={orders} alerts={alerts} />;
}
Red flag to avoid:

Moving the fetches into useEffect on the client, which swaps a server waterfall for a slower one in the browser.

They may ask next:
  • What happens to the whole page if one of the three calls throws?
  • How would you find waterfalls you didn't know were there?
Say it in 60 seconds
Medium Behavioral round Mid-level, Senior Practice question

17. Tell me about a time users saw stale or wrong data in a Next.js app. How did you work out which cache was responsible?

What the interviewer is really testing:
Whether you've debugged Next.js caching in production and can reason through the layers step by step.
Answer frame:

Situation: what users saw and how it was reported.

Investigation: reproduce on a production build, then rule out each cache in turn.

Fix and lesson: the change you made and what stopped it happening again.

Sample spoken answer:

"At my last company, after editors updated a product in the CMS, the listing page kept showing old details for hours, though the product page was fine. It never happened on anyone's laptop, because dev mode doesn't cache the same way, so I first reproduced it with a production build locally. Then I went layer by layer. A hard refresh still showed old data, so it wasn't the browser's router cache. Server logs showed the listing's API call never ran, so the old data was coming from Next's server-side cache. That version of Next cached fetch by default, and the listing fetch had no revalidate or tag, so it stayed cached until the next deploy. The product page's fetch had a tag that the CMS webhook revalidated. I tagged the listing fetch too, and added a review rule: every fetch states its caching on purpose."

Red flag to avoid:

A story where the fix was turning off caching everywhere, with no idea why the data was stale.

They may ask next:
  • How would you make caching behaviour easier for new team members to see?
  • Would you change the caching defaults project-wide, and what would that cost?
Say it in 60 seconds

Actions & APIs 4 questions

Medium Technical round Fresher, Mid-level Practice question

18. What is a Server Action, and how would you use one to handle a simple form submission?

What the interviewer is really testing:
Whether you can do a mutation the App Router way and know what Next does for you around it.
Answer frame:

Definition: an async function marked 'use server' that runs on the server but can be called from a form or client code.

Form: pass it to the form's action prop; it receives the FormData, and it works before JavaScript loads.

After the write: validate, save, then revalidate or redirect; show pending and errors with useFormStatus or useActionState.

Sample spoken answer:

"A Server Action is an async function with 'use server' that runs only on the server, but I can hand it to a form or call it from a Client Component as if it were local. Under the hood Next turns the call into a POST request. For a subscribe form, I write an action that takes the FormData, reads the email, validates it, saves it and then calls revalidatePath or redirect. In the page, I set the form's action to that function. If the form is rendered by a Server Component, it even works before the JavaScript has loaded, which is progressive enhancement for free. For pending states and error messages, I use useFormStatus inside the submit button, or useActionState to get back what the action returned, like a validation message to show next to the field."

Code:
// app/subscribe/actions.ts
'use server';
import { redirect } from 'next/navigation';

export async function subscribe(formData: FormData) {
  const email = String(formData.get('email') ?? '').trim();
  if (!email.includes('@')) return;
  await db.subscriber.create({ data: { email } });
  redirect('/subscribe/thanks');
}

// in a server page:
// <form action={subscribe}><input name="email" /><button>Join</button></form>
Red flag to avoid:

Thinking a Server Action is private because it isn't a visible API route, and skipping validation.

They may ask next:
  • Why must a Server Action check who is calling it, even if the page is protected?
  • How would you show a validation error next to the field?
Say it in 60 seconds
Medium Technical round Mid-level Practice question

19. When would you write a Route Handler instead of a Server Action?

What the interviewer is really testing:
Whether you know which tool fits which caller: your own UI versus outside clients, webhooks and non-page responses.
Answer frame:

Server Actions: mutations triggered from your own React UI, like forms and buttons.

Route Handlers: route.ts files exporting GET, POST and so on, using the standard Request and Response.

Pick handlers for: webhooks, mobile or third-party clients, public APIs, file downloads, feeds and other non-page responses.

Sample spoken answer:

"Server Actions are for my own UI changing data: a form submit, a like button. Next wires up the request, and they work neatly with revalidation and redirects. Route Handlers are route.ts files where I export functions named after HTTP methods, and they take a standard Request and return a Response. I reach for them when the caller isn't my React UI: a payment provider's webhook, a mobile app hitting a JSON API, a cron job, an RSS feed, a CSV export or an OAuth callback. They give me full control over the URL, method, status codes and headers, which actions don't. I also avoid calling my own Route Handlers from Server Components; that's an extra network hop to my own server, so I call the shared data function directly. And a folder can't have both a page and a route file at the same level."

Red flag to avoid:

Fetching your own Route Handlers from Server Components, adding a pointless network round trip.

They may ask next:
  • Are GET Route Handlers cached, and how would you check?
  • How do you verify that a webhook request really came from the provider?
Say it in 60 seconds
Medium Technical round Mid-level, Senior Practice question

20. What is middleware in Next.js good for, and what should you avoid doing in it?

What the interviewer is really testing:
Whether you use middleware for fast request-level decisions and don't treat it as your only line of security.
Answer frame:

What it is: code that runs before a request reaches a route, with a matcher to choose which paths.

Good for: redirects, rewrites, headers and cookies, locale detection, A/B routing, a quick check for a session cookie.

Avoid: slow database calls on every request, and making it the only place authorization happens.

Sample spoken answer:

"Middleware runs before the request is matched to a page, on every path its matcher covers. It can redirect, rewrite to a different route, or add headers and cookies, and then let the request carry on. I use it for things like sending users to their locale, redirecting old URLs, splitting A/B test traffic, or bouncing someone without a session cookie to the login page. What I avoid is heavy work, since it runs on nearly every request and adds latency to all of them, so no big database queries. And I never rely on it as the only authorization check. There was a real vulnerability where a crafted header let requests skip middleware on some self-hosted versions, and it's a good lesson: the data layer and each Server Action should still check the user. In the newest versions the file is called proxy, but the idea is the same."

Red flag to avoid:

Doing all authorization in middleware and trusting every request that gets past it.

They may ask next:
  • How do you stop middleware from running on static files and images?
  • Why might a check in middleware and a check in the page disagree?
Say it in 60 seconds
Hard Situational round Mid-level, Senior Practice question

21. The day before release you notice a deleteAccount Server Action checks login only on the page, not inside the action. What do you do?

What the interviewer is really testing:
Whether you know Server Actions are reachable endpoints and will hold a release for a real security hole.
Answer frame:

Risk: the action can be called with a direct POST, so a check on the page protects nothing.

Fix: read the session inside the action, use the session's user ID, validate input, and refuse by default.

Process: raise it now, keep the fix small and tested, and audit the other actions for the same gap.

Sample spoken answer:

"I treat it as a blocker. A Server Action becomes an endpoint the browser calls with a POST request, so anyone who replays that request can call it without ever loading the protected page. If the action takes an account ID from the form, someone could delete another user's account. I'd flag it to the lead straight away rather than quietly patch it. The fix is small: inside the action, read the session, reject if there's no user, and ignore any account ID sent from the client, using the session's user ID instead. I'd add a test that calls the action without a session and expects a refusal. Then I'd search every 'use server' file and check each action does its own authorization. Longer term I'd suggest a shared helper, so every action starts with the same check."

Red flag to avoid:

Deciding it's fine because the delete button only appears on a protected page.

They may ask next:
  • How does Next.js protect Server Actions against cross-site request forgery?
  • Why shouldn't an action trust an ID sent from the client?
Say it in 60 seconds

Optimisation & SEO 5 questions

Easy Technical round Fresher, Mid-level Practice question

22. How do you set the page title, description and link preview tags in the App Router, including for a dynamic blog post?

What the interviewer is really testing:
Whether you know the Metadata API, how it merges across layouts, and how to build tags from fetched data.
Answer frame:

Static: export a metadata object from a layout or page.

Dynamic: export generateMetadata, which receives params and can fetch the post.

Merging: child segments override parent fields; a title template in the root layout adds the site name.

Sample spoken answer:

"For static pages I export a metadata object from the page or layout with the title, description and openGraph fields, and Next writes the head tags for me. For a blog post, I export an async generateMetadata function. It receives the params, fetches the post, and returns the title, a description from the excerpt, and the cover image for Open Graph. The fetch is memoized, so if the page fetches the same post it isn't called twice; for a direct database call I'd wrap it in React's cache. Metadata merges down the tree, so in the root layout I set a title template that adds the site name after each page's title, and each page only sets its own part. I also add a canonical URL through alternates, and generate the sitemap and robots files with sitemap.ts and robots.ts in the app folder."

Code:
import type { Metadata } from 'next';

export async function generateMetadata(
  { params }: { params: Promise<{ slug: string }> }
): Promise<Metadata> {
  const { slug } = await params;
  const post = await getPost(slug);
  return {
    title: post.title,
    description: post.excerpt,
    openGraph: { images: [post.coverUrl] },
  };
}
Red flag to avoid:

Setting titles in a client-side effect or with hand-written head tags, so crawlers and link previews miss them.

They may ask next:
  • How would you generate a custom social preview image for each post?
  • Can a Client Component export metadata?
Say it in 60 seconds
Easy Technical round Fresher, Mid-level Practice question

23. What does next/image give you over a plain img tag, and what mistakes do people make with it?

What the interviewer is really testing:
Whether you know how the Image component protects Core Web Vitals and how to configure it correctly.
Answer frame:

Benefits: images resized per device in modern formats, lazy loading by default, and reserved space to prevent layout shift.

Required info: width and height, or fill inside a sized parent; a sizes prop for responsive images.

Mistakes: lazy-loading the hero image, missing sizes, and not allowing remote hosts in the config.

Sample spoken answer:

"next/image serves each device an image resized to what it needs, in a modern format like WebP when the browser supports it, and it lazy-loads images below the fold by default. Because it needs a width and height, or the fill prop inside a positioned parent, the browser reserves the space and the layout doesn't jump when the image arrives, which helps CLS. The mistakes I see: leaving the large hero image lazy-loaded, which hurts LCP, so I tell Next to load that one eagerly with high priority. Forgetting the sizes prop on responsive or fill images, so phones download a desktop-sized file. And loading images from a CMS without adding its host to remotePatterns in the config, so they fail. When self-hosting, optimisation runs on your own server, so I keep an eye on CPU and caching there too."

Red flag to avoid:

Using next/image for the hero without marking it high priority, then wondering why LCP got worse.

They may ask next:
  • How does the sizes prop change which file the browser downloads?
  • When would you turn optimisation off for an image?
Say it in 60 seconds
Easy Technical round Fresher, Mid-level Practice question

24. How does next/font help with loading speed and layout shift compared with linking a web font stylesheet?

What the interviewer is really testing:
Whether you know what next/font does at build time and why font loading affects Core Web Vitals.
Answer frame:

Self-hosted: font files are downloaded at build time and served from your own domain, with no request to a font provider.

Less shift: a fallback font is adjusted to match the web font's size, so text barely moves when it swaps.

Scoped use: load it once in a layout and apply it through a className or a CSS variable.

Sample spoken answer:

"With a normal stylesheet link to a font provider, the browser fetches CSS from another domain, then the font files, and when the font finally arrives the text reflows because the fallback was a different size. next/font moves that work to build time. For Google Fonts, it downloads the files and serves them from my own domain, so there's no third-party request from the visitor's browser. It also generates a fallback font with adjusted size metrics, so the swap barely shifts the layout. I call the font function once, usually in the root layout, pick only the subsets and weights I need, and apply it with the className it returns, or as a CSS variable if I'm using Tailwind. For fonts I have as files, next/font/local does the same job."

Red flag to avoid:

Loading every weight and subset of a font, or adding a separate stylesheet link on top of next/font.

They may ask next:
  • Why is it a mistake to call the font function inside every component that uses the font?
  • What does the display option control?
Say it in 60 seconds
Hard Technical round Mid-level, Senior Practice question

25. A page ships far more client JavaScript than it should. How do you find out what's in the bundle and shrink it?

What the interviewer is really testing:
Whether you measure before optimising and know the App Router levers that cut client code.
Answer frame:

Measure: run the bundle analyzer on a production build to see which modules dominate each route.

Common causes: 'use client' too high in the tree, heavy libraries in client files, barrel files pulling in whole packages.

Fixes: push client boundaries down, move work to Server Components, lazy-load with next/dynamic, load third-party scripts with next/script.

Sample spoken answer:

"I start by measuring. I run the bundle analyzer on a production build, which gives a treemap of which modules take the space on each route, and I check the network tab for what the page really downloads. Then I look for the usual culprits. Most often, 'use client' sits on a whole page or layout, so everything below it ships to the browser, and pushing it down to the interactive leaves removes a lot. Next come heavy libraries in client files: a date library, a chart library or a markdown parser. If that work can happen on the server, I move it into a Server Component so it never ships. If it's needed in the browser but not right away, like a chart below the fold or a rich editor in a modal, I load it with next/dynamic. Third-party scripts like chat widgets go through next/script with a lazy strategy. Then I re-run the analyzer and compare."

Red flag to avoid:

Optimising by guesswork, or wrapping everything in next/dynamic without first checking what's actually big.

They may ask next:
  • What's a barrel file, and why can it bloat a bundle?
  • How would you stop the bundle growing again after you've fixed it?
Say it in 60 seconds
Medium Behavioral round Mid-level Practice question

26. Tell me about a time you improved how a Next.js site appeared in search results or link previews. What did you change?

What the interviewer is really testing:
Whether you've used the Metadata API and rendering choices to fix a real SEO problem, and checked the result.
Answer frame:

Problem: what was wrong, like generic titles, blank previews or pages not indexed.

Changes: metadata, rendering mode, sitemap, canonical URLs.

Proof: how you checked it, like the raw HTML, a link preview checker or search console data.

Sample spoken answer:

"At my last company, shared links to our articles showed the site's generic title and no image, and many articles weren't getting indexed. The article page fetched its content in a client component with useEffect, so crawlers and link preview bots saw an empty shell and the default metadata. I moved the fetch into the Server Component, added generateMetadata to build the title, description and Open Graph image from each article, and made the pages ISR so they stayed fast and still updated. I also added a sitemap.ts listing every article with its last-modified date, and canonical URLs so query strings didn't create duplicate pages. I checked each change by viewing the raw HTML with JavaScript turned off and running links through a preview checker. Over the next few weeks, search console showed far more articles indexed, and shared links looked right."

Red flag to avoid:

Claiming SEO improved without any way of checking what crawlers actually received.

They may ask next:
  • How would you add structured data for articles in the App Router?
  • How do you stop filter and sort query strings from creating duplicate pages?
Say it in 60 seconds

Deployment 4 questions

Medium Technical round Fresher, Mid-level Practice question

27. How do environment variables work in Next.js? Why would a NEXT_PUBLIC_ value still be old after you change it on the server?

What the interviewer is really testing:
Whether you know which variables reach the browser and that public ones are fixed at build time.
Answer frame:

Server-only by default: plain variables are readable in server code and never sent to the browser.

NEXT_PUBLIC_ prefix: the value is inlined into the client bundle at build time.

Consequence: changing a public value needs a rebuild; secrets must never carry the prefix.

Sample spoken answer:

"Next loads variables from .env files and the real environment. By default they're only available in server code, like Server Components, Route Handlers and Server Actions, so an API key stays on the server. If I want a value in the browser, I prefix it with NEXT_PUBLIC_, and at build time Next replaces every reference with the literal value inside the JavaScript. That's the catch: the value is baked in when the build runs. If I change it in production and just restart, the browser code still has the old one, because nothing rebuilt it. So public values need a rebuild per environment, or I read them on the server at request time and pass them down as props. Anything with the prefix is visible to anyone who opens dev tools, so it's for public keys only. I also import server-only in modules that hold secrets, so a client import fails the build."

Red flag to avoid:

Putting a secret key behind NEXT_PUBLIC_ to fix an undefined value in a Client Component.

They may ask next:
  • How would you build one Docker image and run it in staging and production with different public settings?
  • What does the server-only package actually do?
Say it in 60 seconds
Medium Technical round Mid-level, Senior Practice question

28. What's the difference between the Edge runtime and the Node.js runtime, and how do you choose one for a route?

What the interviewer is really testing:
Whether you understand the trade-off of running close to users versus close to your data, beyond 'edge is faster'.
Answer frame:

Node.js runtime: full Node APIs and the whole npm ecosystem; the default for routes.

Edge runtime: a smaller set of web-standard APIs, quick to start and close to users, but no file system and many packages won't run.

Choice: weigh where the data lives, which libraries you need, and how much start-up time matters.

Sample spoken answer:

"The Node.js runtime is the default. It has the full Node API, the file system, native modules and every database driver, so it just works. The Edge runtime is a lighter environment built on web-standard APIs like fetch, Request and Response. It starts quickly and can run in many locations close to the user, but there's no file system, many Node libraries don't run, and there are tighter limits on code size and execution. The trap is thinking edge is always faster. If an edge function near the user calls a database on another continent, every query crosses that distance, and a few queries in a row can be slower than a Node function sitting next to the database. So I use edge for light work that doesn't touch the database, like redirects, geolocation or header logic, and keep data-heavy routes on Node, close to the data."

Red flag to avoid:

Moving every route to the edge for speed without looking at where the database lives.

They may ask next:
  • How do you set the runtime for a single route?
  • What would make you move an edge route back to Node?
Say it in 60 seconds
Hard System design round Senior Practice question

29. How would you run Next.js on your own servers, say in Docker behind a load balancer? What breaks that a managed platform handles for you?

What the interviewer is really testing:
Whether you've thought about what Next.js needs from its host, especially caching and consistency across several instances.
Answer frame:

Build: standalone output for a small image that runs with node server.js, or a static export if no server features are needed.

Shared cache: the ISR and data cache sit in each instance's memory and disk by default, so several instances need a shared cache handler.

Around it: a CDN for static assets, image optimisation load on your servers, and the same build on every instance.

Sample spoken answer:

"I set output to standalone in the config, which traces only the files the server needs, so the Docker image copies that folder plus the static assets and runs node server.js. If the site had no server features at all, a static export to plain files would be simpler. The part that bites teams is caching. By default the ISR and data cache live in each instance's memory and on its local disk. With three containers behind a load balancer, revalidating on one leaves the other two serving old pages, and a new container starts cold. So I configure a custom cache handler backed by something shared, like Redis. I also put a CDN in front for the hashed static files, make sure every instance runs the same build so users don't get mismatched assets mid-deploy, and watch CPU, because image optimisation now runs on my own servers."

Red flag to avoid:

Scaling to several containers without a shared cache and then serving different content from each.

They may ask next:
  • What do you lose with a static export?
  • How do you handle users who have an old version of the page open during a deploy?
Say it in 60 seconds
Medium Behavioral round Fresher, Mid-level Practice question

30. Tell me about a Next.js change that worked locally but broke after deploy. What was different, and how did you find it?

What the interviewer is really testing:
Whether you understand the gaps between next dev and a production build and debug them systematically.
Answer frame:

The break: what failed and who noticed.

The gap: what differs between dev and production, such as build-time values, static rendering, runtime or caching.

Prevention: the check you added, like a production build in CI or a preview deploy.

Sample spoken answer:

"In my final-year project, I changed a feature flag and the new banner didn't show in production, though it worked perfectly with next dev. The flag was read from a NEXT_PUBLIC_ variable in a Client Component. I'd changed the value in the hosting settings and restarted, but public variables are inlined into the bundle at build time, so the old value was baked into the JavaScript. I confirmed it by searching the built files and finding the old value there. The quick fix was a rebuild, but the real lesson was that anything I want to change without a build shouldn't live in a public variable. I moved the flag to a server-read variable and passed it to the component as a prop. After that I added a production build and a quick smoke test on a preview deploy before merging, because dev mode hides a lot of these differences."

Red flag to avoid:

Blaming the hosting platform without being able to say what actually differed between local and production.

They may ask next:
  • What other differences between next dev and next start have you run into?
  • How do you test a production build before it reaches users?
Say it in 60 seconds
Were you asked something else? Share it A person checks every question before it goes on the site. No name is shown.
For the call itself

The questions above are the prep. The call has ten more.

ClapAssist is an AI interview assistant for Mac and Windows. It listens to the interview on your computer and shows you what to say, in short lines you can read while you talk. Your resume and notes are never stored on our servers. It stays out of screen share on every plan; only you can see it.

Download ClapAssist with 10 free minutes
Mac and Windows · Stays out of screen share · No card