Loading...
Loading...
App Router, Server Components, rendering strategies, and production patterns SDE-2 interviews expect.
Next.js App Router architecture: SSG, SSR, ISR, CSR, React Server Components, data fetching, middleware, Server Actions, streaming, and performance optimizations.
Next.js App Router, hybrid rendering (SSG, SSR, ISR, CSR), React Server Components, data fetching, middleware, Server Actions, API routes, streaming, prefetching, and built-in optimizations with next/image and next/font.
Next.js is the production React framework SDE-2 frontend interviews expect you to know deeply — not just syntax, but rendering strategies, Server Components, and when to reach for client boundaries.
This module covers App Router file-based routing, hybrid rendering, RSC, data fetching patterns, middleware, Server Actions, and the optimizations that ship with next/image, next/font, and next/script.
Interviewers often ask where to draw the client boundary, how caching works with fetch in Server Components, and how middleware differs from client-side route guards — all covered with decision tables and code examples.
Learn file-based routing, route groups, loading.tsx streaming, generateStaticParams for SSG, dynamic rendering, Server Actions for mutations, and built-in optimizations that ship with every Next.js production app.
Next.js powers most modern React production apps. SDE-2 interviews test whether you understand Server vs Client Components, when to use each rendering strategy, and how App Router differs from the legacy Pages Router. You should also explain caching semantics with fetch(), route segment config, and where middleware runs in the request lifecycle.
Vercel's full-stack React framework optimized for production.
Next.js is a full-stack React framework built on React with file-based routing, hybrid rendering, and server-side capabilities — the default choice for production React apps in 2026.
Hybrid stack — React UI, bundler, Node.js server, Edge runtime.
Next.js ├─ React (UI framework) ├─ Turbopack / Webpack (bundling) ├─ Node.js (server-side rendering) ├─ Edge Runtime (middleware, edge functions) └─ Built-in optimizations (image, font, script)
SSG, SSR, ISR, and CSR — pick based on freshness vs performance.
SSG — Static Site Generation
Pages pre-built at build time as static HTML. Deployed to CDN — fastest possible delivery.
Use case: Docs, blogs, marketing pages
// app/page.tsx — static by default export default function Page() { return <h1>Static Page</h1>; } // Builds to static HTML at build time
SSR — Server-Side Rendering
Page generated on each request on the Node.js server. Fresh data per request.
Use case: Real-time data, user-specific dashboards
// app/dashboard/page.tsx — Server Component export default async function Page() { const data = await fetch("https://api.com/data", { cache: "no-store", // force SSR — no cache }); const json = await data.json(); return <div>{json.title}</div>; }
ISR — Incremental Static Regeneration
Pre-built like SSG, then rebuilt in background after revalidate interval when requested.
Use case: Product pages, news articles
// Route-level revalidation export const revalidate = 60; // Or per-fetch const data = await fetch(url, { next: { revalidate: 60 }, });
CSR — Client-Side Rendering
Minimal HTML from server; full interactivity rendered in browser via JavaScript.
Use case: Highly interactive apps, real-time dashboards
"use client"; export default function Page() { const [data, setData] = useState(null); useEffect(() => { fetch("/api/data") .then((r) => r.json()) .then(setData); }, []); return <div>{data?.title}</div>; }
| Strategy | When Rendered | Performance | Use Case |
|---|---|---|---|
| SSG | Build time | Fastest (CDN) | Static content |
| SSR | Each request | Medium (server) | Real-time, user-specific |
| ISR | Build + rebuild | Fast + fresh | Product pages, news |
| CSR | Browser | Slowest (JS) | Highly interactive apps |
File-based routing with nested layouts, route groups, and dynamic segments.
app/
├── layout.tsx # Root layout
├── page.tsx # /
├── about/
│ └── page.tsx # /about
├── products/
│ ├── page.tsx # /products
│ └── [id]/
│ └── page.tsx # /products/:id
└── (marketing)/
└── analytics/
└── page.tsx # /analytics (route group)| Feature | Syntax | Example |
|---|---|---|
| Fixed routes | page.tsx | /about |
| Dynamic segments | [param]/page.tsx | /products/:id |
| Catch-all | [...slug]/page.tsx | /docs/a/b/c |
| Route groups | (group)/page.tsx | Organize — no URL change |
| Parallel routes | @slot/page.tsx | Simultaneous views |
| Intercepting | (..)modal/page.tsx | Modal-like UI |
Nested Layouts
// app/layout.tsx (root) export default function RootLayout({ children }) { return ( <html> <body> <Navbar /> {children} <Footer /> </body> </html> ); } // app/products/layout.tsx (nested) export default function ProductsLayout({ children }) { return ( <div> <ProductSidebar /> {children} </div> ); }
Partial rendering
// app/products/[id]/page.tsx — Next.js 16 type PageProps = { params: Promise<{ id: string }>; }; export default async function ProductPage({ params }: PageProps) { const { id } = await params; return <div>Product {id}</div>; }
Server Components by default — zero client JS for data-heavy UI.
React Server Components render on the server only — they never ship JavaScript to the client. They can access databases and APIs directly and compose Client Components for interactivity.
// Server Component (default — no directive) export default async function Page() { const posts = await db.query("SELECT * FROM posts"); return <PostList posts={posts} />; } // Client Component — interactivity only "use client"; export default function PostList({ posts }) { const [expanded, setExpanded] = useState(false); return ( <div> {posts.map((post) => ( <PostCard key={post.id} post={post} expanded={expanded} /> ))} </div> ); }
Server fetch, client fetch, and client cache libraries.
// Server Component — async/await (preferred) export default async function Page() { const res = await fetch("https://api.com/posts", { next: { revalidate: 60 }, // ISR: revalidate every 60s }); const posts = await res.json(); return <PostList posts={posts} />; }
// Client Component — useEffect "use client"; export default function Page() { const [posts, setPosts] = useState([]); useEffect(() => { fetch("/api/posts") .then((r) => r.json()) .then(setPosts); }, []); return <PostList posts={posts} />; }
| Method | Location | When | Use Case |
|---|---|---|---|
| Server fetch | Server Component | Request time | SSR, static/ISR data |
| useEffect | Client Component | After mount | CSR, browser-only data |
| React Query | Client Component | Client cache | API caching, deduping |
| SWR | Client Component | Client cache | Lightweight alternative |
next/image, next/font, and next/script — production defaults.
Image Optimization
import Image from "next/image"; <Image src="/hero.jpg" alt="Hero" width={800} height={600} quality={80} loading="lazy" priority={false} />
Font Optimization
import { Inter } from "next/font/google"; const inter = Inter({ subsets: ["latin"] }); export default function Page() { return <h1 className={inter.className}>Hello</h1>; }
Script Optimization
import Script from "next/script"; <Script src="https://analytics.com/script.js" strategy="lazyOnload" />
| Strategy | When It Loads |
|---|---|
| beforeInteractive | Before page is interactive |
| afterInteractive | After page is interactive (default) |
| lazyOnload | During browser idle time |
| worker | Web Worker (experimental) |
Runs on Edge Runtime — auth, redirects, A/B tests, geolocation.
// middleware.ts import { NextResponse } from "next/server"; import type { NextRequest } from "next/server"; export function middleware(request: NextRequest) { // Authentication if (!request.cookies.get("token")) { return NextResponse.redirect(new URL("/login", request.url)); } // Redirect if (request.nextUrl.pathname === "/old") { return NextResponse.redirect(new URL("/new", request.url)); } } export const config = { matcher: ["/api/:path*", "/dashboard/:path*"], };
Mutations without boilerplate API routes — plus traditional REST handlers.
Server Actions (Next.js 13+)
// app/actions.ts "use server"; export async function createPost(formData: FormData) { const title = formData.get("title") as string; await db.insert("posts", { title }); } // app/page.tsx import { createPost } from "./actions"; export default function Page() { return ( <form action={createPost}> <input name="title" /> <button type="submit">Create</button> </form> ); }
No API route needed
Traditional API Routes
// app/api/posts/route.ts import { NextResponse } from "next/server"; export async function GET() { const posts = await db.query("SELECT * FROM posts"); return NextResponse.json(posts); } export async function POST(request: Request) { const data = await request.json(); const post = await db.insert("posts", data); return NextResponse.json(post, { status: 201 }); }
Progressive loading and instant navigation.
// Streaming with Suspense import { Suspense } from "react"; export default function Page() { return ( <div> <Header /> <Suspense fallback={<Loading />}> <SlowComponent /> </Suspense> <Footer /> </div> ); }
Streaming sends HTML progressively — users see the shell immediately while slow components load in the background.
import Link from "next/link"; <Link href="/about" prefetch={true}> About </Link>
Link prefetches route data in the background when visible in the viewport — navigation feels instant.
Quick reference for interview comparisons.
| Feature | Pages Router (Legacy) | App Router (Modern) |
|---|---|---|
| Directory | pages/ | app/ |
| Data fetching | getStaticProps, getServerSideProps | async Server Components |
| Default components | Client only | Server Components |
| Layouts | _app.js wrapper | Nested layout.tsx |
| Streaming | No | Yes (Suspense) |
| Route groups | No | Yes — (group)/ |
| Parallel routes | No | Yes — @slot/ |
| Recommended | Legacy | Modern default |
Common interview questions about Next.js.