All Posts
Next.js

Next.js App Router: A Practical Guide for 2025

February 14, 2025

The App Router replaced the old Pages Router as the default in Next.js, and it changes more than just the folder structure — it changes how you think about rendering. Here's the mental model that finally made it click for me.

Everything Is a Server Component by Default

Every component inside app/ renders on the server unless you explicitly opt out with 'use client' at the top of the file. That single line is the boundary between server and client — everything below it, and everything it imports, ships to the browser.

File-Based Layouts

app/
  layout.tsx      // shared shell
  page.tsx        // route: /
  blog/
    layout.tsx    // nested shell for /blog/*
    page.tsx       // route: /blog
    [slug]/
      page.tsx     // route: /blog/my-post

Layouts persist across navigations within their segment — they don't re-render when you move between child pages, which is why they're the right place for things like a sidebar or nav bar.

Data Fetching Without useEffect

export default async function Page() {
  const posts = await fetch('https://api.example.com/posts').then(r => r.json());
  return <PostList posts={posts} />;
}

Server components can be async directly. No loading state, no useEffect, no client-side waterfall — the HTML arrives already populated.

When You Actually Need 'use client'

  • Anything using useState, useEffect, or other hooks
  • Event handlers like onClick
  • Browser-only APIs (window, localStorage)

The best pattern is pushing client components as far down the tree as possible — keep the shell and data-fetching on the server, and isolate interactivity into small leaf components.