← Back to Writing
5 min read

Building with the Next.js App Router


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.