App Router & Architecture

Interview Questions: RSC, Routing, and Rendering

Q1: Explain the difference between Server Components and Client Components. When would you use each?

Concept: React Server Components (RSC) render exclusively on the server. Their code is not included in the client-side bundle. Client Components are standard React components that are hydrated on the client.

Usage:

  • Server Components (Default): Use for fetching data, accessing backend resources (DB), keeping sensitive info (API keys), and reducing client bundle size.
  • Client Components ('use client'): Use for interactivity (onClick, onChange), using State/Effects (useState, useEffect), or using browser-only APIs (localStorage, window).

Gotcha: You cannot import a Server Component into a Client Component directly. You must pass it as a children prop (Composition Pattern).

Q2: How does the App Router handle Layouts vs Templates?

Layout (layout.js): Wraps pages and preserves state on navigation. It does not re-render when navigating between sibling routes.

Template (template.js): Similar to a layout, but it creates a new instance for each child on navigation. State is reset.

Use Case: Use templates for features that rely on useEffect (like logging page views) or animation libraries (framer-motion) that need to trigger on every page transition.

Q3: What are Parallel Routes and Intercepting Routes?

Parallel Routes (@folder): Allow rendering multiple pages in the same layout simultaneously (e.g., a dashboard with @team and @analytics slots).

Intercepting Routes ((.)folder): Allow loading a route within the current layout while keeping the URL context (e.g., opening a photo in a modal when clicked from a feed, but as a full page on refresh).

Q4: How does Next.js handle Static vs Dynamic Rendering?

Static Rendering (Default): Routes are rendered at build time. Data is cached indefinitely.

Dynamic Rendering: Routes are rendered at request time. This happens automatically if you use:

  • Dynamic functions: cookies(), headers().
  • searchParams prop in a page.
  • no-store fetch requests.

Forcing Dynamic: export const dynamic = 'force-dynamic'

Q5: What are Route Handlers?

Definition: Route Handlers allow you to create custom request handlers for a given route using the Web Request and Response APIs. They replace API Routes from the Pages Router.

File: Defined in a route.js or route.ts file inside the app directory.

// app/api/users/route.js
export async function GET(request) {
  const data = await fetchUsers();
  return Response.json({ data });
}

Supported Methods: GET, POST, PUT, PATCH, DELETE, HEAD, and OPTIONS.

Q6: How does Data Revalidation work in the App Router?

Revalidation allows you to update cached data without rebuilding the entire site.

  • Time-based Revalidation: Automatically revalidate data after a certain amount of time has passed.
    fetch('...', { next: { revalidate: 3600 } })
  • On-demand Revalidation: Manually revalidate data based on an event (e.g., form submission).
    revalidatePath('/blog') or revalidateTag('posts')

Q7: What is the purpose of Middleware in Next.js?

Function: Middleware allows you to run code before a request is completed. You can modify the response by rewriting, redirecting, modifying request headers, or responding directly.

Common Use Cases:

  • Authentication and Authorization (protecting routes).
  • Server-side Redirects (e.g., based on user role or locale).
  • A/B Testing (rewriting to different variants).
  • Bot protection.

Location: Defined in middleware.js at the root of your project.

Q8: What is the `generateStaticParams` function?

Purpose: It is the replacement for getStaticPaths in the App Router.

Usage: It allows you to generate static routes at build time for dynamic segments (e.g., /blog/[slug]).

export async function generateStaticParams() {
  const posts = await getPosts();
  return posts.map((post) => ({
    slug: post.slug,
  }));
}

Q9: How do you handle errors in the App Router?

File: Create an error.js file in the route segment.

Mechanism: It automatically wraps the page in a React Error Boundary. If an error occurs during rendering, the error.js component is displayed instead of crashing the whole app.

Recovery: The component receives a reset() function prop to attempt to re-render the segment.