All posts

Next.js App Router: Six Months In

March 15, 2026 6 min
Next.jsReactTypeScript

Six months ago I migrated this portfolio (and a client project) from the Next.js Pages Router to the App Router. I was cautious — the App Router had a reputation for rough edges early on — but I'm glad I made the move.

Here's an honest account of what changed, what surprised me, and what I'd tell myself at the start.

What Changed Everything: Server Components

The mental model shift from client-first to server-first took about two weeks to click.

In the Pages Router, everything was a client component by default. You'd fetch data in getServerSideProps or getStaticProps, then pass it as props. It was simple but required a lot of ceremony.

In the App Router, components are server components by default. You can await directly in the component body:

// This runs on the server — no useEffect, no useState, no props drilling
export default async function BlogPage() {
  const posts = await getBlogPosts(); // reads from the filesystem
 
  return (
    <div>
      {posts.map(post => <BlogCard key={post.slug} post={post} />)}
    </div>
  );
}

No extra API layer, no data-fetching hook, no loading state to manage. The server does the work and sends HTML.

The biggest App Router win: you stop thinking "how do I get this data to the client?" and start thinking "does this need to be on the client at all?"

What Tripped Me Up

The use client boundary. Server components can import and render client components, but not the reverse. I kept accidentally trying to use useState in a server component or import a server-only function into a client component. TypeScript doesn't catch this at compile time (though React does at runtime with a clear error).

Caching is explicit and composable. In Pages Router, ISR was revalidate: 60 in getStaticProps. In App Router, you opt into caching at the fetch level or with unstable_cache. Powerful, but the mental model takes adjustment.

next/font requires server components. If you try to load a font in a client component, you get a build error. Move fonts to layout.tsx.

What I'd Do Differently

Start with the App Router if you're building new. The Pages Router is going nowhere (Next.js maintains it), but new patterns and Next.js features are App Router-first.

For migration: migrate leaf components first, then work up to pages. Don't try to do a big-bang migration of a large app — it's not worth it.

The Bottom Line

The App Router is the right direction. The performance wins from server components are real. The data-fetching story is cleaner once the mental model clicks. And co-locating server logic with UI components — rather than hiding it in getStaticProps — makes code much easier to follow.

Six months in, I wouldn't go back.

Written by Pranay Gupta