← BackNext.js29 Q

Next.js

29 questions | entry to principal | answers written to be said out loud, not read

entry

What do page.tsx and layout.tsx actually do in the App Router?

page.tsx makes a route segment publicly reachable, it is the leaf UI for that URL. layout.tsx wraps that page and any nested routes, and it stays mounted across navigations inside it so state like a sidebar or scroll position survives. Every folder in the app directory maps to a URL segment, and only page.tsx or route.ts actually makes it navigable, a folder with just a layout is not a page on its own.

What do loading.tsx, error.tsx, and not-found.tsx give you for free?

loading.tsx is shown automatically while a Server Component in that segment is fetching data, React just wraps the segment in Suspense for you. error.tsx catches rendering errors in that segment and its children without a manual try catch in every component, and it has to be a Client Component because it uses a reset function. not-found.tsx renders when I call the notFound function or navigate to a path Next cannot match, and template.tsx behaves like layout.tsx but remounts on every navigation instead of persisting.

What is the actual difference between a Server Component and a Client Component?

A Server Component renders on the server, it can read a database or the filesystem directly, and its code never ships to the browser. A Client Component renders on the client too, it can use state, effects, and browser APIs, and its code does end up in the JavaScript bundle. Everything in the App Router is a Server Component by default, I only add the client directive when I actually need interactivity or a browser only API.

What does next/image actually optimize for you?

It automatically resizes and serves images in a modern format like webp based on the device requesting them, so I am not shipping a huge desktop image to a phone. It lazy loads images outside the viewport by default and reserves the right amount of space up front using the width and height I give it, which avoids layout shift. For anything above the fold, like a hero image, I still have to mark it priority myself or it will lazy load and hurt my largest contentful paint.

What problem does next/font solve?

It downloads a font at build time and self hosts it alongside my app instead of the browser making a separate request to something like Google Fonts, so there is no extra network round trip and no third party tracking request. It also sets the right font metrics up front to avoid layout shift while the font loads. I import it once, call the loader with the weights I need, and apply the resulting className to my root layout.

How do environment variables work in Next.js, and what does NEXT_PUBLIC do?

A plain environment variable is only available on the server, inside Server Components, route handlers, and Server Actions, and it never reaches the browser bundle. Prefixing one with NEXT_PUBLIC bakes it into the client bundle at build time, so it is genuinely public and I never put a secret behind that prefix. I keep real secrets like database credentials and API keys as server only variables and only expose the handful of values, like a public analytics id, that are actually safe for anyone to see.

junior

What is a route.ts file and when do you reach for it instead of a Server Action?

route.ts defines a request handler for a segment, I export functions named after HTTP methods like GET or POST and it behaves like an API endpoint rather than a page. I reach for a route handler when something outside my own app needs to call it, like a webhook, a public API, or an OAuth callback. For a form inside my own app I usually prefer a Server Action, it skips writing a fetch call and a matching endpoint.

What does the use client directive actually do?

It marks the boundary where a module and everything it imports gets included in the client bundle and becomes interactive in the browser. It does not mean the component only renders on the client, a Client Component still gets server rendered for the first paint and then hydrates. I put the directive as low as possible in the tree, on the actual interactive piece, so I do not drag a whole page into the client bundle by putting it in a layout.

How do dynamic segments and catch-all routes work in the App Router?

A folder named with square brackets like id captures one URL segment and hands it to the page as a param. A catch-all segment written with three dots captures every remaining segment as an array, and an optional catch-all also matches the parent route with no segments at all. I use a plain dynamic segment for something like a product page and a catch-all when I am building something like a documentation site with arbitrary nested paths.

Why did Next.js move from the pages router to the App Router?

The pages router only supported client side data fetching patterns like getServerSideProps and getStaticProps, and every component in a page shipped to the client whether it needed interactivity or not. The App Router is built around React Server Components, so I can fetch data directly inside a component that never ships to the browser, and I only pay the client bundle cost for the pieces that truly need it. It also added real nested layouts, streaming, and Suspense support, which the pages router never had natively.

How do you fetch data in the App Router?

I fetch directly inside an async Server Component, I just await the call and use the result in the returned JSX, there is no useEffect or loading state to wire up by hand. Next extends the native fetch function so I can control caching and revalidation right at the call site. When I need several independent requests I kick them off with Promise.all so they run concurrently instead of waterfalling one after another.

How does the fetch cache work in Next.js?

By default Next caches the result of a fetch call and reuses it across requests, which is what makes a route eligible for static rendering. I control that behavior per call, cache no store opts a request out of caching entirely, and next revalidate with a number of seconds gives it a time based cache. This is separate from the browser's own HTTP cache, it is Next's own data cache sitting on the server.

What does generateStaticParams do?

It tells Next which values a dynamic segment should be pre-rendered for at build time, so a route like a product slug can generate real static pages for every known product instead of rendering on demand. Any param I did not list still gets rendered the first time it is requested, and Next can cache that result depending on my dynamicParams setting. I use it for content that does not change often and where I know the full set of values ahead of time, like blog posts or product pages.

What is use server and what is a Server Action?

The use server directive marks a function as a Server Action, it runs on the server but I can call it directly from a Client Component or wire it straight to a form's action prop without writing a route handler and a fetch call myself. Next handles the network request under the hood, and I usually pair it with revalidatePath or revalidateTag so the UI reflects the mutation right away. I still validate input and check authorization inside the action itself, calling it from the client does not make it any more trustworthy than an API route.

How does the metadata API and generateMetadata work?

I export a static metadata object from a page or layout for title, description, and other tags that do not depend on data. When the metadata needs data, like a product name in the title, I export an async generateMetadata function instead and it receives the same params the page does. Metadata from nested layouts and pages merges together, with the most specific segment winning for anything that overlaps.

How do you generate a dynamic Open Graph image?

I add an opengraph-image file in the route segment, either a static image or a tsx file that exports a default function returning JSX, and Next renders that JSX to an actual image at request time using its image generation runtime. It picks up the right size and content type automatically and wires the meta tags for me, so I do not hand write og image markup. I use the dynamic version when the image needs real data, like a blog post title rendered onto a template.

How do you handle errors in the App Router?

For expected failures, like a not found record or a failed validation, I handle them explicitly in the Server Component or Server Action and render the right UI myself, or call notFound. For unexpected rendering failures I rely on error.tsx, which acts as an error boundary for that segment and its children, it has to be a Client Component since it needs a reset function to let the user retry. I keep error boundaries granular, at the segment level, so one broken widget does not take down the whole page.

senior

What are parallel routes and intercepting routes for?

Parallel routes use a folder named with an at sign to render more than one page inside the same layout at once, each slot has its own loading and error state. Intercepting routes render a route inside the current layout, for example as a modal, while the actual URL still points at the full page. I reach for this combination for the classic pattern where clicking a photo opens a modal, but refreshing that URL loads the full standalone photo page.

What can middleware do in Next.js, and what can't it do?

Middleware runs before a request matches a route, so it is good for things like redirects, rewrites, reading or setting cookies, and simple auth checks based on a token. It runs on the edge runtime by default, so it cannot use Node only APIs like the filesystem, and it should stay fast since it runs on every matched request. I do not treat it as a place for real authorization logic, I still check permissions again on the server for anything sensitive, middleware is a fast gate, not the source of truth.

Edge runtime versus Node runtime, how do you choose?

The Node runtime gives me the full Node API surface, so I use it for anything that needs a native database driver, the filesystem, or heavier compute. The edge runtime is a lighter, faster starting environment that runs closer to the user, but it only supports a smaller set of web standard APIs. I default to Node unless I have a specific reason to reach for edge, like low latency middleware or a simple route that only needs fetch.

What decides whether a route is rendered statically or dynamically?

A route can be rendered at build time and reused for every request if nothing in it opts out, that is static rendering. The moment a route reads something request specific, like cookies, headers, or search params, or calls fetch with no store, Next has to render it fresh on every request, that is dynamic rendering. I try to keep routes static by default and push the truly dynamic parts into a smaller Client Component or a Suspense boundary instead of making the whole page dynamic.

How does ISR work with revalidatePath and revalidateTag?

Incremental static regeneration lets a statically rendered page get refreshed on a timer without a full rebuild, I set that with the revalidate option on a fetch call or a route segment. revalidatePath and revalidateTag let me invalidate that cache on demand instead of waiting for the timer, for example after a Server Action updates a record I call revalidatePath on that page so the next visitor gets fresh data. Tag based revalidation is more precise, I tag the fetch calls that read a piece of data and only invalidate that tag instead of a whole path.

How does streaming with Suspense work in the App Router?

Instead of waiting for every piece of data on a page before sending anything, Next can send the shell of the page immediately and stream in slower parts as they finish, wrapping a slow component in Suspense gives it its own fallback while the rest of the page is already interactive. This means one slow query does not block the whole page from showing up. I reach for it whenever a page has one clearly slower data dependency, like a heavy analytics widget next to fast core content.

Why do cookies or headers make a route dynamic?

Calling cookies or headers reads something specific to the incoming request, so Next cannot safely reuse one cached response for every visitor. As soon as a Server Component calls either of those, Next opts that whole route out of static rendering and renders it fresh per request. If I only need that request specific data in a small part of the page, I isolate it in its own component behind a Suspense boundary instead of letting it force the entire route to be dynamic.

What causes hydration errors and how do you fix them?

A hydration error happens when the HTML React rendered on the server does not match what it renders on the client during the first pass. Common causes are using something like Date.now or Math.random directly in render, checking window or localStorage before mounting, invalid HTML nesting like a div inside a p, or a browser extension mutating the DOM before React attaches. I fix it by moving anything environment specific into an effect so it only runs after mount, or by rendering a stable placeholder on the first pass.

Why does every Client Component have a real cost?

Anything marked as a Client Component, plus everything it imports, gets bundled and shipped down to the browser, parsed, and hydrated, even if most of it never actually changes after the first render. If I put the boundary too high in the tree, like on a whole page, I drag static content and heavy dependencies into the client bundle along with the one interactive button that actually needed it. I keep Server Components as the default and push the client boundary down to the smallest leaf that truly needs state or a browser API.

staff

What are the different caching layers in Next.js?

There is the request memoization cache, which dedupes identical fetch calls made during a single render pass. There is the data cache, which is the persistent fetch cache I control with revalidate and cache options. There is the full route cache, which stores the rendered result of a static route at build time, and the router cache on the client, which holds recently visited route segments so back and forward navigation feels instant. Each layer has its own invalidation rules, which is why a stale page after a mutation is almost always a caching layer I forgot to invalidate, not a bug.

What happens when you run next build, and how does that affect deployment?

next build compiles the app, statically renders every route it can, and produces a manifest describing which routes are static, which are dynamic, and which need a server function at request time. On a platform that maps to that model, static routes become CDN assets and dynamic routes become serverless or edge functions automatically. If I deploy it somewhere else I need a Node server actually running next start, or I use the standalone output mode to get a minimal self contained server for something like a container.

principal

When is Next.js the wrong choice?

If I am building a simple static marketing site with no real data needs, a plain static site generator is less machinery than the App Router's caching and rendering model. If the app is a heavy client side tool like a design editor or a dashboard that lives entirely behind a login with almost no SEO need, a plain single page app can be simpler since I am not getting much benefit from server rendering anyway. And if the team does not want to reason about server versus client components and the caching layers that come with them, that complexity has a real cost, and a simpler framework might ship faster.

Fast recall

RSC = component rendered on the server | use client = opts into the client bundle | use server = marks a Server Action | ISR = regenerate a static page on a timer | generateStaticParams = pre-builds dynamic routes | revalidateTag = invalidate cached fetches by tag | revalidatePath = invalidate cache for one path | Suspense = streams in a loading fallback | middleware = runs before the route matches | edge runtime = lightweight runtime, fewer apis | route.ts = a request handler, not a page | hydration = attaching interactivity to server html | NEXT_PUBLIC = env var exposed to the browser

BH·Next.js·github.com/bunlongheng/study