Ch. 24

Next.js interview questions & answers

The React framework interviewers ask about: App Router, Server Components and Server Actions, rendering strategies (SSR, SSG, ISR), caching, routing and deployment.

30 interview questions20 quiz questions8 notes
your progress0%

Notes in this chapter

Filter all notes →

30 Next.js interview questions study by subtopic

30 questions
  1. 1.What is the difference between the App Router and the Pages Router in Next.js?easy

    Both are file-system routers, but they are built on different React models. The Pages Router (pages/) renders every page as a Client Component that is also server-rendered, and fetches data with page-level functions such as getServerSideProps, getStaticProps and getStaticPaths.

    The App Router (app/) is built on React Server Components. Components are server-only by default, any component can be async and fetch its own data, and layouts are nested and persist across navigations. It adds loading.tsx and error.tsx conventions, streaming with Suspense, Server Actions and a layered cache.

    Both routers can live in one project during a migration, but a URL must be owned by only one of them. New projects should use the App Router; I would still expect to maintain Pages Router code in older apps.

    What interviewers listen for
    • App Router is built on React Server Components
    • Pages Router fetches through getServerSideProps / getStaticProps
    • Nested persistent layouts, loading and error files in app/
    • Both can coexist during an incremental migration

    Likely follow-up: How would you migrate one page from pages/ to app/ without a big-bang rewrite?

  2. 2.What is the difference between a Server Component and a Client Component in the App Router?easy

    In the App Router every component is a Server Component unless a 'use client' directive says otherwise. Server Components run only on the server, at build time or per request. They can be async, read a database or secrets directly, and their code never ships to the browser. They cannot use state, effects, event handlers or browser APIs.

    Client Components are pre-rendered to HTML on the server and then hydrated in the browser, so they can use useState, useEffect, event handlers and window. Their code is part of the JavaScript bundle.

    The usual design is server components for data and layout, with small client components as interactive leaves. That keeps the bundle small and keeps secrets on the server.

    What interviewers listen for
    • Server Components are the default
    • Server Components never ship their code to the browser
    • Client Components are still server-rendered, then hydrated
    • Push interactivity down to small leaves

    Likely follow-up: Can a Client Component render a Server Component?

  3. 3.What exactly does 'use client' mark, and how can a Client Component still contain server-rendered content?mid

    'use client' marks a module boundary, not a single component. The file with the directive, and every module it imports, becomes part of the client bundle. You only need it at the entry point where the tree becomes interactive; children imported by that file are client code automatically.

    A Client Component cannot import a Server Component, because the import would pull server code into the browser bundle. It can still render one when the Server Component is passed in as children or another prop from a server parent. The server renders that subtree first and sends the result as part of the RSC payload, so the client wrapper only places it.

    Props crossing the boundary must be serializable by React: plain objects, arrays, dates, promises and Server Actions are fine, ordinary functions and class instances are not.

    What interviewers listen for
    • The directive marks a module and its imports
    • Client cannot import a server module, but can receive it as children
    • Props across the boundary must be serializable
    • Server Actions are the only functions that can cross

    Likely follow-up: Why does putting use client in the root layout hurt performance?

  4. 4.Explain SSR, SSG, ISR and CSR in Next.js and when you would choose each.easy
    • SSG (static generation): HTML is rendered at build time and served from a CDN. Best for content that is the same for every visitor, such as docs or marketing pages.
    • ISR (incremental static regeneration): a static page with a revalidation window or on-demand invalidation. Visitors get cached HTML, and Next regenerates it in the background after it goes stale. Good for product or blog pages that change occasionally.
    • SSR (dynamic rendering): HTML is rendered per request. Needed when output depends on cookies, headers or must always be fresh, such as a dashboard.
    • CSR: the server sends a shell and the browser fetches data. Useful for highly interactive, private widgets.

    In the App Router these are not separate APIs. A route is static unless it uses request data or opts out of caching, and revalidate turns static into ISR.

    What interviewers listen for
    • SSG: built once, served from a CDN
    • ISR: static plus background regeneration
    • SSR: per-request, for personalised or always-fresh data
    • App Router decides static vs dynamic from what the route uses

    Likely follow-up: What happens to the first visitor after an ISR page goes stale?

  5. 5.In the App Router, what makes a route dynamic instead of static, and how do you check which one you got?mid

    By default Next.js prerenders a route at build time. It switches the route to dynamic rendering when, during that render, it sees something that only exists per request:

    • request APIs: cookies(), headers(), draftMode(), connection(), or reading searchParams in a page
    • a fetch with cache: 'no-store' or next: { revalidate: 0 }
    • segment config export const dynamic = 'force-dynamic' or revalidate = 0
    • dynamic segments without generateStaticParams

    A plain fetch with no options does not make the route dynamic in Next.js 15. The route is still prerendered and that response is frozen into the HTML until the route is revalidated.

    To check, read the next build route table: ○ is static, ● is SSG with generateStaticParams, ƒ is dynamic. export const dynamic = 'error' turns an accidental dynamic API into a build error.

    What interviewers listen for
    • Static is the default
    • Request APIs and no-store fetches opt into dynamic
    • Build output symbols: ○ static, ● SSG, ƒ dynamic
    • dynamic = 'error' guards a page that must stay static

    Likely follow-up: Why might a page that reads Date.now() still show the same time for every visitor?

  6. 6.Describe the caching layers in the Next.js App Router.hard

    There are four, and each answers a different question:

    • Request memoization (server, one render): identical GET fetches during a single render are deduplicated. React cache() does the same for database calls.
    • Data Cache (server, across requests and deployments): stores fetch results that opt in with cache: 'force-cache' or next.revalidate. Invalidated by time or by revalidateTag / revalidatePath.
    • Full Route Cache (server): the HTML and RSC payload of static routes, produced at build or on revalidation.
    • Router Cache (browser memory): RSC payloads of visited and prefetched segments, used for instant back and forward navigation.

    When data looks stale I walk the layers from the browser inwards: is the route static, is the fetch cached, and did the mutation call revalidatePath or revalidateTag?

    What interviewers listen for
    • Memoization lasts one render
    • Data Cache persists across requests
    • Full Route Cache holds prerendered HTML and RSC payload
    • Router Cache lives in the browser

    Likely follow-up: Which of these does router.refresh() clear?

  7. 7.Which caching defaults changed in Next.js 15, and why does it matter when upgrading from 14?mid

    Next.js 15 moved from cached-by-default to uncached-by-default in three places:

    • fetch is no longer stored in the Data Cache unless you pass cache: 'force-cache', set next.revalidate, or configure the segment to cache.
    • GET Route Handlers are dynamic by default; export const dynamic = 'force-static' restores static output.
    • The client Router Cache keeps page segments for 0 seconds by default (staleTimes.dynamic: 0), so navigating back to a dynamic page fetches fresh data. Back and forward navigation and shared layouts still reuse the cache.

    When upgrading, an app that relied on implicit caching may hit its backend much more often. I would read the build output, then add explicit force-cache, revalidate or tags where the data really is shareable.

    What interviewers listen for
    • fetch is uncached by default in 15
    • GET Route Handlers are dynamic by default
    • Router Cache staleTimes.dynamic is 0
    • Make caching explicit after the upgrade

    Likely follow-up: If fetch is uncached, why is a page with a plain fetch still marked static?

  8. 8.Compare time-based revalidation with on-demand revalidation using revalidatePath and revalidateTag.mid

    Time-based revalidation sets a freshness window: export const revalidate = 60 on a segment or fetch(url, { next: { revalidate: 60 } }). After 60 seconds the next request still gets the stale copy, and Next regenerates in the background (stale-while-revalidate). It is simple but data can be up to one window old.

    On-demand revalidation invalidates when the data actually changes. revalidatePath('/blog') marks a route's cached output stale. revalidateTag('posts') invalidates every fetch tagged with next: { tags: ['posts'] }, across all routes that used it. You call them from a Server Action or a Route Handler, for example a CMS webhook.

    I use tags when one piece of data appears on many pages, paths when one page owns its data, and a long time window as a safety net.

    What interviewers listen for
    • Time-based is stale-while-revalidate
    • revalidatePath targets a route
    • revalidateTag targets data used across routes
    • Call them from Server Actions or Route Handlers

    Likely follow-up: How would you secure a webhook route that calls revalidateTag?

  9. 9.What are Server Actions and how do they work under the hood?mid

    A Server Action is an async function marked with 'use server' that runs on the server but can be called from the client, typically as a form action or from an event handler. At build time Next replaces the function on the client with a reference ID. Calling it sends a POST to the current page with that ID, Next runs the function, and the response can carry a re-rendered RSC payload.

    Because forms work as plain HTML posts, a form with a Server Action submits even before JavaScript loads. After the mutation, the action can call revalidatePath or revalidateTag and redirect, and the client receives fresh UI in the same round trip.

    In React 19, useActionState gives you the returned state and a pending flag, and useFormStatus exposes pending state inside the form.

    What interviewers listen for
    • 'use server' marks the function as a callable endpoint
    • Invoked through a POST with an action ID
    • Progressive enhancement for forms
    • Revalidate and redirect in the same round trip

    Likely follow-up: When would you still write a Route Handler instead?

  10. 10.How do you secure a Server Action?hard

    Treat every Server Action as a public HTTP endpoint. Anyone who finds its ID can call it with curl and any arguments, whether or not the button is shown to them.

    • Authenticate and authorise inside the action, every time. Hiding a button or checking in middleware is not enough.
    • Validate input with a schema such as Zod; FormData values are untrusted strings.
    • Return only what the UI needs, never raw database rows.

    Next.js adds defences: action IDs are unguessable and change between builds, unused actions are dropped from the build, values captured in an inline action's closure are encrypted, and the framework compares the Origin header with the host to block cross-site posts. Behind a proxy you may need serverActions.allowedOrigins. These help, but none replaces an authorisation check.

    What interviewers listen for
    • An action is a public POST endpoint
    • Authorise inside every action
    • Validate inputs with a schema
    • Origin check and unguessable IDs are defence in depth

    Likely follow-up: Why should you avoid closing over secrets in an inline Server Action?

  11. 11.When would you use a Route Handler (route.ts) instead of a Server Action?mid

    Server Actions are for mutations triggered by your own UI. They are always POST, tied to the React tree and return data to the component that called them.

    Route Handlers (app/api/.../route.ts, the App Router version of pages/api) export functions named after HTTP methods and use Web Request and Response. I use them when:

    • a third party calls me: webhooks, OAuth callbacks, mobile apps
    • I need a specific method, status code, headers or streaming response
    • I serve non-HTML content: JSON for other clients, files, RSS, images
    • I want a cacheable GET endpoint

    A route.ts and a page.tsx cannot live in the same segment. In Next.js 15, GET handlers are dynamic unless you opt into static output.

    What interviewers listen for
    • Actions: UI-driven mutations, always POST
    • Route Handlers: public HTTP APIs and webhooks
    • Route Handlers use Web Request and Response
    • No route.ts next to page.tsx in one segment

    Likely follow-up: Should a Server Component call your own Route Handler to get data?

  12. 12.What is Next.js middleware, and what is it good for?mid

    Middleware is a single middleware.ts file at the project root (or in src/) that runs before a request reaches routes and caches. It receives a NextRequest and can redirect, rewrite to another path, set request or response headers and cookies, or return a response directly.

    Good uses: redirecting signed-out users away from private areas, locale detection, A/B test bucketing with rewrites, and adding security headers. A config.matcher limits which paths it runs on, which matters because it otherwise runs for every request, including static assets and prefetches.

    It has used the Edge runtime by default; the Node.js runtime is stable from Next.js 15.5. It should stay fast: no heavy database work on every request. Next.js 16 renames the file convention to proxy.ts.

    What interviewers listen for
    • Runs before routing and caching
    • Redirect, rewrite, headers, cookies
    • Scope it with config.matcher
    • Keep it light; Edge runtime by default

    Likely follow-up: Why is middleware not enough on its own to protect data?

  13. 13.Why should authentication not rely only on middleware?hard

    Middleware is a good first gate for redirects, but it is one layer far from the data. Several things make it unsafe as the only check:

    • Matchers are easy to get wrong, and a new route outside the pattern is silently unprotected.
    • Server Actions and Route Handlers are endpoints in their own right and can be called directly.
    • Middleware usually only checks that a session cookie exists, not that this user may read this record.
    • Framework bugs happen: CVE-2025-29927 let attackers skip middleware with a crafted x-middleware-subrequest header until it was patched in 15.2.3 and backported releases.

    The recommended pattern is a data access layer: functions that verify the session and the user's permission right next to the query, called from pages, actions and handlers alike.

    What interviewers listen for
    • Middleware is an optimistic first gate
    • Authorise next to the data
    • Actions and handlers need their own checks
    • CVE-2025-29927 showed middleware can be bypassed

    Likely follow-up: How would you share the session check between a page and a Server Action?

  14. 14.Name the special files in an App Router segment and what each one does.easy

    A folder becomes a route segment, and these files give it behaviour:

    • page.tsx: the UI for the URL; without it the segment is not routable.
    • layout.tsx: wraps the page and child segments, persists across navigations. The root layout must render html and body.
    • template.tsx: like a layout but remounts on every navigation.
    • loading.tsx: wraps the page in a Suspense boundary and shows instantly while it streams.
    • error.tsx: a Client Component error boundary for the segment.
    • not-found.tsx: rendered when notFound() is called.
    • route.ts: an HTTP endpoint instead of a page.
    • default.tsx: fallback for parallel route slots.

    They nest in a fixed order: layout, template, error, loading, not-found, then the page.

    What interviewers listen for
    • page makes a segment routable
    • layout persists, template remounts
    • loading and error wrap the page in boundaries
    • route.ts is an endpoint, not a page

    Likely follow-up: Why does an error.tsx not catch errors thrown in the layout of the same segment?

  15. 15.What is the difference between layout.tsx and template.tsx?mid

    A layout is rendered once and preserved while you navigate between its child routes. Its client state, scroll position in a sidebar and DOM are kept, and it does not re-render on navigation. That is why a layout cannot read the current pathname or searchParams as props: it would not update.

    A template wraps children the same way but gets a new key for each navigation, so React unmounts and remounts it. State resets and effects run again.

    Use a template when you need per-page enter animations, an effect that should fire on every navigation such as logging a page view, or a form that must reset. Otherwise prefer layouts, since preserved state and fewer re-renders are the point of nested routing.

    What interviewers listen for
    • Layout persists and does not re-render on navigation
    • Template remounts on each navigation
    • Layouts do not receive searchParams
    • Templates for animations or per-navigation effects

    Likely follow-up: How would a layout highlight the active nav link if it does not re-render?

  16. 16.How do loading.tsx and Suspense enable streaming in the App Router?mid

    Streaming sends HTML in chunks. The server flushes the parts of the page that are ready and leaves placeholders for parts still waiting on data; when those finish, it streams the HTML plus a small script that swaps them into place.

    loading.tsx is shorthand for wrapping the segment's page in a Suspense boundary with that fallback, so the layout and loading UI appear immediately. For finer control, wrap slow components in your own Suspense boundaries so a slow recommendations panel does not hold back the product details.

    Benefits: faster time to first byte and first paint, and earlier hydration of the parts that are ready. A caveat: once the first chunk is sent the HTTP status is fixed, so a notFound() inside a streamed boundary cannot change it to 404.

    What interviewers listen for
    • HTML is flushed in chunks as data resolves
    • loading.tsx wraps the page in Suspense
    • Granular Suspense boundaries isolate slow parts
    • Status code is committed once streaming starts

    Likely follow-up: Where would you call notFound() to guarantee a real 404?

  17. 17.How does error handling work with error.tsx, global-error.tsx and not-found.tsx?mid

    error.tsx must be a Client Component, because it is a React error boundary. It wraps the segment's page and children and receives error and reset. It does not wrap the layout.tsx of the same segment, so a layout error bubbles to the parent's error.tsx. Errors in the root layout are caught by app/global-error.tsx, which replaces the root layout and must render its own html and body.

    In production, errors thrown in Server Components reach the client with a generic message and a digest you can match against server logs, so internals do not leak.

    notFound() throws a special error that renders the nearest not-found.tsx. Expected failures, such as validation errors in a Server Action, should be returned as values, not thrown.

    What interviewers listen for
    • error.tsx is a client error boundary
    • It does not catch its own segment layout
    • global-error.tsx for the root layout
    • Production hides server error messages behind a digest

    Likely follow-up: What does reset() actually do?

  18. 18.What causes a hydration error in Next.js and how do you fix one?mid

    Hydration attaches React to server-rendered HTML. React expects the first client render to produce the same markup; if it differs, React 19 reports that the server-rendered HTML did not match and re-renders that tree on the client (minified as error #418 in production).

    Common causes:

    • values that differ per environment: Date.now(), Math.random(), locale or time zone formatting
    • branching on typeof window during render
    • reading localStorage during render
    • invalid nesting such as a div inside a p
    • browser extensions editing the DOM

    Fixes: render the same thing on both sides, then update in useEffect; format dates with an explicit time zone; load client-only widgets with next/dynamic and ssr: false from a Client Component; and use suppressHydrationWarning only for a single unavoidable text node such as a timestamp.

    What interviewers listen for
    • First client render must match the server HTML
    • Time, randomness, locale and window checks are usual causes
    • Move client-only values into useEffect
    • suppressHydrationWarning is a narrow escape hatch

    Likely follow-up: Why is if (typeof window !== "undefined") in render a problem but fine inside useEffect?

  19. 19.How do you avoid request waterfalls when fetching data in Server Components?hard

    A waterfall happens when independent requests run one after another: a page awaits the user, then awaits orders, then a child awaits recommendations. Each await in sequence adds a full round trip.

    Fixes:

    • Start independent requests together and await them with Promise.all, or Promise.allSettled if one may fail.
    • Start a promise in the parent and pass it to a child, which unwraps it with React use() inside a Suspense boundary, so the shell streams while data loads.
    • Let each component fetch what it needs and rely on request memoization or React cache() to deduplicate shared calls, instead of prop-drilling.
    • Use sequential awaits only when one request depends on another's result.

    I confirm with server timing logs or tracing that the slowest request, not their sum, sets the response time.

    What interviewers listen for
    • Sequential awaits add round trips
    • Promise.all for independent work
    • Pass promises down and stream with Suspense
    • Deduplicate with memoization or cache()

    Likely follow-up: What is the downside of Promise.all if one request is much slower than the others?

  20. 20.Two Server Components on the same page call getUser(). How many requests hit the backend, and how do you control that?hard

    If getUser() uses fetch with the same URL and options, it hits the backend once: Next.js memoizes GET fetches for the duration of a single server render, even with cache: 'no-store'. The memo is discarded when the render ends, so the next request fetches again.

    If getUser() queries a database or uses a client library, there is no automatic dedupe. Wrap it in React cache() to get the same per-request memoization:

    • export const getUser = cache(async (id) => db.user.find(id))

    Memoization only applies inside the React component tree, not in Route Handlers, and it is not cross-request caching. For that you need the Data Cache, unstable_cache, or the experimental 'use cache' directive. This is why fetching where data is used is fine in the App Router.

    What interviewers listen for
    • Same GET fetch in one render runs once
    • Works even with no-store
    • Wrap non-fetch calls in React cache()
    • Per request only, not a cross-request cache

    Likely follow-up: Why does cache() need to be called at module level and not inside the component?

  21. 21.Why are params, searchParams, cookies() and headers() asynchronous in Next.js 15?mid

    In Next.js 15 these became Promises: const { slug } = await params and const jar = await cookies(). The reason is that the server can start rendering and prerendering everything that does not depend on the request, and only wait at the exact point where request data is read. That is the foundation for partial prerendering, where a static shell is served immediately and dynamic holes stream in.

    For migration, Next.js 15 still lets you read them synchronously, but logs a warning in development such as "params should be awaited before using its properties". A codemod (npx @next/codemod@canary next-async-request-api .) rewrites most call sites. In Client Components, use React use(params) or the useParams and useSearchParams hooks.

    What interviewers listen for
    • params and searchParams are Promises in 15
    • cookies() and headers() must be awaited
    • Lets static parts render before request data is read
    • Sync access warns in 15; a codemod exists

    Likely follow-up: How does generateMetadata receive params in Next.js 15?

  22. 22.What does generateStaticParams do, and what happens for a slug it did not return?mid

    generateStaticParams is the App Router replacement for getStaticPaths. In a dynamic segment such as app/blog/[slug]/page.tsx it returns the list of params to prerender at build time, for example [{ slug: 'a' }, { slug: 'b' }]. The build output marks the route with ●.

    For a slug not in the list, behaviour depends on dynamicParams:

    • true (default): the page is rendered on the first request and then cached like the others, similar to fallback: 'blocking'.
    • false: unknown slugs return 404, like fallback: false.

    For very large catalogues, a common approach is to return only the most popular slugs, or even an empty array, and let the rest render on demand. That keeps build times manageable.

    What interviewers listen for
    • Replaces getStaticPaths
    • Listed params are prerendered at build
    • dynamicParams decides what unknown params do
    • Prerender the popular subset for big sites

    Likely follow-up: How would you revalidate one blog post after it is edited in a CMS?

  23. 23.What does the next/image component do for you?easy

    Image from next/image wraps the img element with optimisations:

    • Resizing: it generates a srcset so the browser downloads a size that fits the viewport, served by the image optimisation endpoint.
    • Modern formats: WebP by default, AVIF if enabled in images.formats.
    • Lazy loading by default for images below the fold.
    • Layout stability: width and height, or fill with a sized parent, reserve space and prevent layout shift.

    For the hero or LCP image, set priority so it is preloaded instead of lazy-loaded. Remote images must be allowed in images.remotePatterns so the optimiser cannot be abused as an open proxy. With output: 'export', the default loader is unavailable, so you need a custom loader or unoptimized.

    What interviewers listen for
    • Responsive srcset and resizing
    • Lazy by default; priority for the LCP image
    • width/height or fill prevent layout shift
    • remotePatterns restrict external sources

    Likely follow-up: Why is the sizes prop important when using fill?

  24. 25.How do you set titles, descriptions and Open Graph tags in the App Router?easy

    Use the Metadata API in a layout.tsx or page.tsx, which must be Server Components:

    • a static export const metadata = { title, description, openGraph }
    • or export async function generateMetadata({ params }) when it depends on data, such as a product name

    Metadata merges from the root layout down, so the root can set title.template like %s | Shop and metadataBase for absolute URLs, while pages fill in their own title. A fetch inside generateMetadata is memoized with the page's identical fetch, so the data is not loaded twice.

    File conventions cover the rest: opengraph-image.tsx, icon.png, sitemap.ts and robots.ts. The Pages Router equivalent is the Head component from next/head.

    What interviewers listen for
    • metadata object or generateMetadata
    • Merged from layouts down to pages
    • title.template and metadataBase in the root
    • File conventions for OG images, sitemap and robots

    Likely follow-up: Why can metadata not be exported from a Client Component?

  25. 26.How do environment variables work in Next.js, and what is special about NEXT_PUBLIC_?easy

    Next.js loads .env, .env.local, .env.development and .env.production (plus their .local variants) into process.env on the server. Variables already set in the real environment take precedence, and .env.local is skipped when NODE_ENV is test.

    By default variables are server-only: reading process.env.DB_URL in a Client Component gives undefined. Prefixing a variable with NEXT_PUBLIC_ makes it available in the browser, but the value is inlined into the JavaScript bundle at build time. Changing it later in the deployment environment has no effect until you rebuild, and anyone can read it.

    So: never put secrets behind NEXT_PUBLIC_, and if you build one image for several environments, read config at runtime on the server and pass what the client needs as props.

    What interviewers listen for
    • Server-only by default
    • NEXT_PUBLIC_ is inlined at build time
    • Public variables are visible to anyone
    • Rebuild needed to change a public value

    Likely follow-up: How would you run the same Docker image in staging and production with different public API URLs?

  26. 27.Explain parallel routes and intercepting routes, and how they combine to build a modal.hard

    Parallel routes use named slots: folders like @modal or @analytics. Each slot is passed to the parent layout as a prop alongside children and navigates independently. When a slot has no match on a full page load, Next renders its default.tsx, so you usually add one returning null.

    Intercepting routes use (.), (..) or (...) folder prefixes to render a different route in the current layout during client navigation. app/@modal/(.)photo/[id]/page.tsx intercepts /photo/123 when you click a thumbnail.

    Together: clicking a photo shows it in a modal over the feed, the URL becomes /photo/123 and is shareable, back closes the modal, and a refresh or direct visit renders the full app/photo/[id]/page.tsx instead.

    What interviewers listen for
    • @slot folders become layout props
    • default.tsx for unmatched slots on hard load
    • (.) prefixes intercept on soft navigation only
    • Shareable modal URLs; refresh shows the full page

    Likely follow-up: Why does the modal disappear when the user refreshes?

  27. 28.What do [slug], [...slug], [[...slug]] and (group) folders mean in the App Router?easy
    • [slug]: a dynamic segment. /blog/hello gives params as { slug: 'hello' }.
    • [...slug]: catch-all. /docs/a/b gives { slug: ['a', 'b'] }; /docs alone does not match.
    • [[...slug]]: optional catch-all. Also matches /docs, with slug undefined.
    • (group): a route group. The folder name is left out of the URL, so app/(marketing)/about/page.tsx serves /about. Groups let you give sections different layouts, or several root layouts, without changing URLs.
    • _folder: a private folder, excluded from routing, handy for colocated components.

    In Next.js 15, params is a Promise in pages and layouts, so you write const { slug } = await params.

    What interviewers listen for
    • Brackets create dynamic segments
    • Catch-all vs optional catch-all
    • Route groups do not appear in the URL
    • Private folders start with an underscore

    Likely follow-up: What happens if two route groups both define /about?

  28. 29.How do you guarantee that server-only code, such as a module holding an API key, never ends up in the client bundle?mid

    Server Components are not shipped to the browser, but a shared utility can be imported into a Client Component by mistake, and then it is bundled. Three safeguards:

    • Add import 'server-only' at the top of the module. If any client module imports it, the build fails with a clear error instead of silently leaking.
    • Keep secrets in non-NEXT_PUBLIC_ variables, which are undefined in the browser.
    • Use a data access layer that returns DTOs: only the fields the UI needs, never whole user rows with password hashes.

    Next.js also has experimental React taint APIs (experimental.taint) that throw if a marked object or value is passed to a Client Component. The mirror package client-only marks modules that must never run on the server.

    What interviewers listen for
    • import 'server-only' fails the build on misuse
    • Secrets without NEXT_PUBLIC_
    • Return DTOs, not raw records
    • Taint APIs as an experimental extra check

    Likely follow-up: Why are props passed to a Client Component visible in the page source?

  29. 30.What changes when you self-host Next.js on several containers instead of deploying to Vercel?hard

    next start supports every feature, and output: 'standalone' produces a minimal server folder that fits well in a Docker image. The surprises come with more than one instance:

    • The Data Cache and ISR output live on each instance's disk and memory by default. One pod revalidates, the others keep serving old pages. Configure a shared cacheHandler (for example Redis) and usually cacheMaxMemorySize: 0.
    • Server Actions encrypt closure values with a key generated per build. Set the same NEXT_SERVER_ACTIONS_ENCRYPTION_KEY on every instance and use a consistent build ID.
    • Old clients may call action IDs that a new deployment no longer has, so plan for version skew during rollouts.
    • Image optimisation runs in your process and uses CPU and memory.

    output: 'export' is a different choice: pure static files with no server features.

    What interviewers listen for
    • standalone output for containers
    • Shared cacheHandler for ISR across instances
    • Same encryption key and build ID everywhere
    • Plan for version skew and image CPU

    Likely follow-up: Which features stop working with output: export?

Prefer multiple choice? All 20 Next.js MCQs with answers →

esc