Top Next.js Interview Questions & Answers

Next.js interviews test your understanding of its rendering strategies and the App Router's server-first model — the parts that go beyond plain React.

Take Practice Interview Now
Improve your chances of getting selected — rehearse these answers out loud with an AI interviewer and get a scored report in minutes.
Start Free →

1. Why does Next.js exist when React already handles UI rendering?

React by itself is just a UI library — it gives you components and a rendering model, but it doesn't solve routing, data fetching, bundling, or where your code actually runs. Next.js is a framework built on top of React that adds file-based routing, built-in code splitting, server rendering, and an opinionated build pipeline so you're not hand-wiring webpack and a router from scratch. It also solves the SEO and initial-load-performance problems that pure client-side React apps have, since a plain React SPA ships an empty `<div id="root">` and renders everything in the browser. On top of that Next.js gives you image optimization, middleware, API routes, and now React Server Components, letting you build full-stack apps without reaching for a separate backend for simple things. Basically, React answers how do you build components, and Next.js answers how do you ship a production app.

2. What's the difference between the App Router and the Pages Router?

The Pages Router is the original Next.js routing system where every file in the `pages` directory becomes a route, and data fetching is done through special exported functions like `getServerSideProps` or `getStaticProps`. The App Router, introduced in Next.js 13 and stable since 13.4, lives in the `app` directory and is built around React Server Components, so components are server-rendered by default and can be `async` functions that fetch data directly. The App Router also introduces nested layouts, colocated loading and error states via `loading.js` and `error.js`, and route groups, none of which exist natively in the Pages Router. Data fetching happens with plain `fetch` calls inside server components instead of dedicated lifecycle functions, and caching is controlled per request rather than per page. Most new projects use the App Router now since that's where Next.js is investing, but the Pages Router is still fully supported and plenty of production codebases haven't migrated.

3. What are Server Components and Client Components, and what does "use client" do?

In the App Router, every component is a React Server Component by default, meaning it renders on the server, never ships its JavaScript to the browser, and can directly access backend resources like databases or the filesystem. A Client Component is one that opts into running in the browser as well, so it can use hooks like `useState` or `useEffect`, attach event handlers, or use browser-only APIs. You mark a component as a Client Component by adding the `"use client"` directive at the top of the file, which tells the bundler to include that component and everything it imports in the client JavaScript bundle. The split matters for performance because server components keep your bundle size down and let you fetch data close to the source without shipping fetching logic to the client. A common pattern is to keep pages and layouts as server components and push interactivity down into small, leaf-level client components rather than marking a whole page `"use client"`.

4. What are SSR, SSG, ISR, and CSR, and when would you use each?

These are the four core rendering strategies Next.js supports. SSG, static site generation, renders pages to HTML at build time, so it's fastest and cheapest to serve but the content is fixed until the next build — great for marketing pages or blog posts that don't change often. SSR, server-side rendering, renders the page on every request, which fits content that's personalized or must always be fresh, like a dashboard tied to a logged-in user. ISR, incremental static regeneration, gives you the speed of static pages but lets you set a `revalidate` time so Next.js regenerates the page in the background after it goes stale, which is the sweet spot for something like a product catalog that changes occasionally. CSR, client-side rendering, fetches and renders data entirely in the browser after the initial page load, which suits highly interactive, non-SEO-critical sections like an authenticated settings panel where you don't need the content indexed or available on first paint.

5. How does data fetching work in the App Router compared to the Pages Router?

In the Pages Router you fetch data through dedicated functions — `getStaticProps` for build-time data, `getServerSideProps` for per-request data — that run only on the server and pass their return value into the page as props. In the App Router, server components can be declared `async` and fetch data directly in the component body with a plain `await fetch(...)` or a database call, no special export needed. Next.js extends the native `fetch` API with caching options, so you control behavior with things like `cache: 'force-cache'` for static-like behavior or `next: { revalidate: 60 }` for ISR-style revalidation, all set at the call site instead of the page level. This means different fetches on the same page can have different caching strategies, which wasn't really possible in the Pages Router since the whole page shared one data-fetching method. You can also fire off multiple `await` calls without awaiting immediately, or use `Promise.all`, to fetch in parallel and avoid request waterfalls.

6. How does file-based routing work in Next.js?

In both routers, the file system defines your routes instead of a separate route configuration file. In the App Router, folders inside `app` map to URL segments, and a `page.tsx` file inside a folder is what actually makes that segment navigable — a folder without a `page.tsx` is just a layout or organizational segment, not a page. Dynamic segments use square brackets, like `app/blog/[slug]/page.tsx` for a single dynamic param, `[...slug]` for a catch-all, and `[[...slug]]` for an optional catch-all. Special files like `layout.tsx`, `loading.tsx`, `error.tsx`, and `not-found.tsx` in a folder automatically apply to that route segment and everything nested under it. There's also route groups, folders wrapped in parentheses like `(marketing)`, which let you organize routes or share a layout across a subset of pages without affecting the actual URL path.

7. What's the difference between getStaticProps, getServerSideProps, and how does the App Router replace them?

`getStaticProps` runs at build time in the Pages Router and produces static HTML plus a JSON payload that gets reused for every request until the next build or an ISR revalidation kicks in. `getServerSideProps` runs on every single request on the server, so the page is always fresh but slower to respond since it can't be cached the same way. Both only exist in the Pages Router and are exported alongside the page component, and Next.js calls them automatically before rendering. In the App Router there's no equivalent exported function at all — instead you make the component itself `async` and fetch data inline, and the caching behavior that used to be decided by which function you picked is now decided by the `fetch` cache options or route segment config like `export const dynamic = 'force-dynamic'` or `export const revalidate = 60`. So the mental model shifts from picking a lifecycle function to picking a caching behavior per fetch.

8. What are API routes and how do they work in the App Router?

In the Pages Router, any file under `pages/api` automatically becomes a serverless API endpoint that exports a default handler function receiving `req` and `res`. In the App Router, the equivalent is a `route.ts` file inside the `app` directory, called a Route Handler, where you export named functions per HTTP method — `GET`, `POST`, `PUT`, `DELETE`, and so on — instead of one catch-all handler. Route handlers use the standard Web `Request` and `Response` APIs rather than the Node-style `req`/`res` objects, which makes them portable across runtimes like Edge and Node. They're useful for webhooks, form submissions, or any endpoint you need to call from client components or external services, though for data fetching from your own server components you typically don't need a route handler at all since the server component can just call the database or fetch directly. You also can't have both `page.tsx` and `route.ts` in the same folder segment, since they'd conflict.

9. What is Next.js middleware and what's it used for?

Middleware in Next.js is code that runs before a request completes, defined in a single `middleware.ts` file at the root of your project or inside `src`, and by default it executes on the Edge runtime for low latency. It gets access to the incoming request and can rewrite, redirect, modify headers, or return a response early, all before the matched route ever renders. Common use cases are authentication checks, like redirecting unauthenticated users away from protected routes, A/B testing by rewriting to different variants, geolocation-based redirects, or attaching custom headers for logging. You scope which paths it runs on using an exported `config.matcher`, since running it on every single request including static assets would be wasteful. Because it runs on the Edge runtime, it can't use full Node APIs the way a server component or route handler can, though newer versions of Next.js have started allowing a Node.js runtime option for middleware too.

10. How does next/image optimize images, and why not just use a regular img tag?

The `next/image` component automatically serves images in modern formats like WebP or AVIF when the browser supports them, resizes images to match the actual rendered dimensions, and lazy-loads any image below the fold by default. It requires you to specify `width` and `height`, or use `fill`, which lets Next.js reserve the correct space in the layout before the image loads, directly preventing the cumulative layout shift you get with a plain `img` tag whose dimensions aren't known until the file downloads. Under the hood, images are served through an optimization endpoint, either Next's built-in optimizer or a configured external loader, that generates and caches resized versions on demand rather than shipping one giant file to every device. It also generates a responsive `srcset` automatically so mobile devices don't download desktop-sized images. The tradeoff is that this optimization needs a server runtime to happen on the fly, so for fully static exports you sometimes have to configure a custom loader or disable optimization.

11. How do layouts and nested layouts work in the App Router?

A `layout.tsx` file wraps the `page.tsx` in the same folder and any nested routes below it, and it persists across navigations between those routes, so it doesn't re-render or lose state like scroll position or an open dropdown when you move from one child page to another. The root `layout.tsx` at the top of the `app` directory is required and must contain the `<html>` and `<body>` tags, since the App Router doesn't have a separate `_document.js` file like the Pages Router does. Layouts nest by folder structure, so if you have a layout in `app/dashboard/layout.tsx` and another in `app/dashboard/settings/layout.tsx`, a page at `app/dashboard/settings/page.tsx` renders wrapped inside both, outer to inner. Layouts receive a `children` prop that Next.js fills in with whatever matched route segment comes next, and they can be server components that fetch shared data, like a sidebar that needs the current user, without every child page having to refetch it. This is a real improvement over the Pages Router, where persistent layouts meant manually wrapping pages through a custom `_app.js` pattern.

12. What is generateStaticParams and how does it relate to getStaticPaths?

`generateStaticParams` is the App Router's replacement for `getStaticPaths` from the Pages Router, used on dynamic route segments to tell Next.js which param values should be pre-rendered at build time. You export it from the same `page.tsx` that defines the dynamic segment, and it returns an array of objects whose keys match the bracketed folder name, like `{ slug: 'my-post' }` for `app/blog/[slug]/page.tsx`. Unlike `getStaticPaths`, there's no separate `fallback` option to configure — any params not returned by `generateStaticParams` are rendered dynamically on demand and then cached, similar to ISR's fallback-blocking behavior, unless you explicitly opt out with `dynamicParams = false`. It composes naturally with the rest of the App Router's data model, so the same fetch used to get the list of valid slugs inside `generateStaticParams` is often reused inside the page component itself, benefiting from Next's automatic request deduplication. This is commonly used for things like blog posts or product pages where you know the full set of valid paths ahead of time.

13. How does Next.js handle SEO metadata, and what's the Metadata API?

The App Router has a built-in Metadata API where you export either a static `metadata` object or an async `generateMetadata` function from a `page.tsx` or `layout.tsx`, and Next.js uses it to inject the right `<title>`, `<meta>`, and Open Graph tags into the `<head>`. `generateMetadata` is useful when the metadata depends on data, like generating a page title from a blog post fetched by slug, and it can be `async` just like the page component itself. Metadata defined in a layout applies to all nested pages unless a more specific page overrides it, and Next.js merges parent and child metadata sensibly, for example combining title templates. In the Pages Router, the equivalent was manually rendering a `next/head` component inside each page, which was more error-prone since you had to remember to include it everywhere. The Metadata API also handles generating `sitemap.xml` and `robots.txt` through special files, and takes care of things like viewport and favicon tags automatically.

14. What are Server Actions and what problem do they solve?

Server Actions are functions marked with the `"use server"` directive that let you run server-side code, like a database mutation, directly from a client or server component without manually creating a Route Handler and calling `fetch` from the client. You can pass a Server Action directly to a form's `action` prop, and Next.js handles the network request behind the scenes, including progressive enhancement so the form still works before JavaScript hydrates. They solve the boilerplate problem of writing an API endpoint just to handle a form submission or a simple mutation, and they integrate with `revalidatePath` or `revalidateTag` so you can invalidate cached data right after the mutation completes. Because the function body only ever runs on the server, sensitive logic and credentials never get bundled into client JavaScript, which is safer than doing the same work in a client component that talks to an external API. They're commonly used for things like an add-to-cart button, a comment form, or a settings update, anywhere you'd otherwise reach for a small API route.

15. How do environment variables work in Next.js?

Next.js reads environment variables from `.env.local`, `.env.development`, `.env.production`, and `.env` files, loading them at build time and, for server-only variables, at request time as well. By default, any variable is only available on the server; to expose one to the browser bundle you have to prefix it with `NEXT_PUBLIC_`, which tells Next.js to inline its value into the client JavaScript at build time. This distinction exists so you don't accidentally leak secrets like database URLs or API keys into code that ships to every visitor's browser. Because `NEXT_PUBLIC_` variables get inlined at build time, changing one requires a rebuild, it's not something you can swap at runtime the way you might with a server-only variable read from `process.env`. It's also worth knowing that `.env.local` is meant for local overrides and gitignored by default, while `.env` is typically committed and holds shared defaults.

16. What's the difference between next/link and a regular anchor tag?

`next/link` wraps an `<a>` tag but hooks into the Next.js client-side router, so navigating between pages doesn't trigger a full page reload — the router swaps out the content while keeping shared layouts and client state intact. It also automatically prefetches the linked page's code and, in the App Router, its RSC payload, when the link scrolls into the viewport in production, so by the time a user actually clicks, the navigation feels instant. A plain `<a>` tag works for navigation too but forces a full browser reload every time, discarding the whole React tree and losing any client-side state, which is why you'd only reach for it for external links or cases where you deliberately want a full reload, like after changing auth state. Under the hood `Link` still renders a real anchor element, so things like right-click open in new tab, ctrl-click, and crawlability all work exactly as expected. This prefetch-and-soft-navigate behavior is one of the more noticeable performance wins Next.js gives you over a bare React Router setup.

Take Practice Interview Now
Improve your chances of getting selected — rehearse these answers out loud with an AI interviewer and get a scored report in minutes.
Start Free →
Related topics
ReactAngularVue.jsRedux