The Next.js App Router introduced in version 13 and stabilised in 14 is not just a new file convention — it's a fundamentally different mental model.
Layouts are composable
In the Pages Router, every page owned its own layout logic. With the App Router, layouts are nested by the filesystem. A layout.tsx at app/blog/layout.tsx wraps every route under /blog automatically. This means you define chrome once and it never re-mounts when navigating between children.
Server Components by default
Every component in the App Router is a React Server Component unless you opt in with "use client". This matters for performance: server components never ship their JavaScript to the browser. Data fetching lives right inside the component via async/await — no useEffect, no loading state boilerplate for the initial render.
Static generation with generateStaticParams
For dynamic routes like /blog/[slug], generateStaticParams replaces getStaticPaths. The API is simpler: return an array of param objects, and Next.js pre-renders each one at build time.
export function generateStaticParams() {
return blogs.map((b) => ({ slug: b.slug }));
}
What to watch out for
Context providers must be client components. If your state library or theme system uses React context, wrap it in a client component and import it into your root layout — exactly the pattern used in this portfolio's ThemeProvider.
The migration is worth it. Build times are faster, the developer experience is cleaner, and the output is leaner.