Next.js and the App Router
Next.js and the App Router
Next.js is a framework that bundles server rendering, routing, and bundling on top of React.
1. About Next.js
Next.js was built by Guillermo Rauch's team at Vercel (then ZEIT) and first released on October 25, 2016. The license is MIT.
| Version | When | Event |
|---|---|---|
| 1.0 | 2016 | Debuted as an SSR React framework. |
| 9 | 2019 | dynamic routes, API routes. |
| 10 | 2020 | Image component, Internationalized Routing. |
| 12 | 2021 | Switched to the Rust-based SWC compiler. Middleware. |
| 13 | 2022-10 | App Router + RSC beta. Turbopack alpha. |
| 14 | 2023-10 | Server Actions stable. App Router stabilized. |
| 15 | 2024-10 | React 19 RC, Partial Prerendering experimental, async request API. |
| 16 | 2025 | Cache Components (formerly PPR) stabilization in progress. |
App Router was introduced in 13, stabilized in 14, and is the default recommendation in 15 and 16. The earlier Pages Router still works.
2. Filesystem routing
In App Router, folders inside the app/ directory are routes.
app/
layout.tsx // root layout
page.tsx // /
about/page.tsx // /about
blog/[slug]/page.tsx // /blog/:slug
(marketing)/ // group (does not appear in the URL)
pricing/page.tsx
Special files:
layout.tsx— wraps child routes (shared UI).page.tsx— the body of the route.loading.tsx— Suspense fallback.error.tsx— error boundary.not-found.tsx— 404 screen.route.ts— Route Handler (REST/JSON endpoint).
3. React Server Components
In App Router, components are server components (RSC) by default. They run only on the server, are not bundled into the JS payload, and can access the database, the filesystem, and private API keys.
If client interaction is needed, write "use client" at the top of the file. That component and the children it imports are included in the client bundle.
// app/posts/page.tsx (server component, default)
export default async function Page() {
const posts = await db.post.findMany()
return <PostList items={posts} />
}
// app/posts/LikeButton.tsx
"use client"
export function LikeButton() {
const [n, setN] = useState(0)
return <button onClick={() => setN(n+1)}>{n}</button>
}
This boundary is the core mental model of App Router.
4. Rendering modes
| Mode | Abbrev | When |
|---|---|---|
| Client-Side Rendering | CSR | Render in the browser. Default for SPAs. |
| Server-Side Rendering | SSR | Generate HTML on every request. |
| Static Site Generation | SSG | Generate HTML at build time. |
| Incremental Static Regeneration | ISR | SSG + periodic regeneration. |
| Partial Prerendering / Cache Components | PPR | Mix static and dynamic parts inside one page. |
In App Router, the cache option of a data fetch effectively chooses the mode.
fetch(url, { cache: "force-cache" }) // similar to SSG
fetch(url, { cache: "no-store" }) // SSR
fetch(url, { next: { revalidate: 60 } }) // ISR (60 seconds)
From 15 and 16 onwards, dynamic APIs like cookies() and headers() became async and must be explicitly awaited.
5. App Router vs Pages Router
| Item | Pages Router | App Router |
|---|---|---|
| Directory | pages/ |
app/ |
| Route unit | one file = one route | folder + page.tsx |
| Data | getServerSideProps, getStaticProps |
await fetch() directly inside RSC |
| Layout | a single _app.tsx |
nested layout.tsx per folder |
| Default component | client | server |
App Router is the recommended choice for new projects, but Pages Router is still supported.
6. Comparison with other frameworks
| Framework | Base | Position |
|---|---|---|
| Next.js | React, Vercel | The largest React metaframework. |
| Remix | React, Shopify (acquired 2022) | Emphasizes the standard web platform (form actions). Integrated with React Router (7+, 2024). |
| Astro | Custom (1.0 in 2022) | content-first. 0-JS by default, island hydration. |
| Nuxt | Vue | Vue's answer to Next. |
| SvelteKit | Svelte | |
| TanStack Start | React | A new metaframework based on TanStack Router. |
The choice is a trade-off. When SEO, server resource access, and the React ecosystem matter, Next is often discussed; for content sites, Astro; for form-centric apps, Remix/React Router.
7. Getting started and Server Action
pnpm dlx create-next-app@latest my-app
cd my-app
pnpm dev
// app/new/page.tsx
import { redirect } from "next/navigation"
async function create(formData: FormData) {
"use server"
const title = String(formData.get("title"))
await db.post.create({ data: { title } })
redirect("/")
}
export default function Page() {
return (
<form action={create}>
<input name="title" />
<button>저장</button>
</form>
)
}
8. Route Handler
// app/api/health/route.ts
export async function GET() {
return Response.json({ ok: true })
}
From 13, Turbopack (Vercel, Rust) was gradually introduced into the dev server, and from 15+ it is the default in dev. Production builds are also transitioning to Turbopack, with webpack used alongside in the meantime.
9. Common pitfalls
The contagiousness of "use client" — every component that a client component imports is automatically classified as client. When possible, keep client boundaries at the leaves and pass data down via props.
Server-only secrets — environment variable names that start with NEXT_PUBLIC_ are bundled into the client. Never prefix secrets with NEXT_PUBLIC_.
Default of fetch caching — App Router's fetch has changed its default cache policy across versions. From 15, the default is no caching, and you must explicitly write cache or revalidate.
When cookies() and headers() are called — calling these APIs makes the route automatically dynamic. A pitfall is that a page expected to be statically cached unintentionally becomes dynamic.
Pairing with React 19 — Next 15+ pairs with React 19, and some libraries took time to be compatible with 19. Check that the react version matches in the lockfile.
Image component's sizes — when written incorrectly, large images are sent to mobile. A common pitfall in responsive images.
Closing thoughts
App Router's RSC has a large initial overhead. Once it becomes familiar, though, you can reduce the code shipped to the client and gain the freedom to handle the database and secrets directly on the server. Keeping the "use client" boundary at the leaves is the safest flow.
Next
- state-philosophy
- styling-tailwind
We refer to the Next.js official site, Next.js GitHub, App Router learn, Rendering docs, Turbopack, Remix, and Astro.