Skip to main content
Alvin QuachFull Stack Developer
HomeProjectsExperienceBlog
HomeProjectsExperienceBlog
alvinquach

Full Stack Developer building systems that respect complexity.

Open to opportunities

AQ

Projects

  • All Projects
  • Hoparc Physical Therapy
  • OpportunIQ
  • Hoop Almanac
  • SculptQL

Knowledge

  • Blog
  • Experience
  • Interview Prep

Connect

  • Contact
  • LinkedIn
  • GitHub
  • X

Resources

  • Resume
© 2026All rights reserved.
Back to Blogs
Tutorial
Depth: ●●○○○

Next.js App Router Performance: What Actually Moved the Needle

Concrete performance wins from migrating to App Router. Server Components, streaming, parallel data fetching—real metrics from my portfolio and Hoop Almanac, not theoretical benefits.

Published December 2, 20253 min readImportance: ★★★★☆
Share:

Everyone says App Router is faster. But what actually improves? I rebuilt my portfolio with App Router and optimized Hoop Almanac. Here's what moved the metrics.

The Biggest Wins

1. Smaller Client Bundle (Server Components)

In Pages Router, everything ships to the client. In App Router, Server Components stay on the server. My portfolio's project listing page went from 89KB to 34KB JavaScript. The data fetching, formatting, and sorting all happen server-side now.

The pattern: anything that doesn't need interactivity is a Server Component. The project cards themselves are Server Components. Only the filter buttons and search input are Client Components.

2. Streaming with Suspense

Hoop Almanac's player dashboard loads multiple data sources: player stats, predictions, news, trade suggestions. In Pages Router, I'd fetch all data before rendering. Users stared at a spinner.

With App Router, each section is wrapped in Suspense. The page shell renders immediately. Stats load first (fastest query). Predictions stream in next. News and trades load last. Users see progress instead of waiting.

Time to First Byte stayed the same, but perceived performance improved dramatically. Users could start reading player stats while predictions loaded.

3. Parallel Data Fetching

Pages Router's getServerSideProps ran sequentially by default. If you needed three API calls, you'd often await them one after another. App Router encourages parallel fetching.

In my portfolio, the landing page fetches profile, projects, and testimonials. Three separate Server Components, three parallel fetches. Combined latency dropped from ~800ms to ~300ms (limited by the slowest query, not the sum).

Performance Gotchas

Client Component Hydration

Slapping 'use client' on everything defeats the purpose. In Hoop Almanac, I initially made the entire draft board a Client Component (it needs real-time updates). The bundle bloated back up.

The fix: composition. The draft board LAYOUT is a Server Component. Only the interactive pieces (pick button, timer, chat) are Client Components. The player list renders server-side, the selection handler is client-side.

Fetch Caching Surprises

App Router caches fetch() by default. Great for static data, confusing for dynamic data. In OpportunIQ, diagnostic results were showing stale data because the AI response was cached.

The fix: explicit cache control. Use { cache: 'no-store' } for dynamic data, or revalidate tags for ISR-like behavior. Don't assume—be explicit about caching strategy for each fetch.

Real Metrics from My Portfolio

Before (Pages Router): LCP 2.4s, FCP 1.8s, JS Bundle 156KB. After (App Router): LCP 1.1s, FCP 0.9s, JS Bundle 67KB. Lighthouse Performance: 72 → 94.

The gains came from: Server Components (smaller bundle), parallel fetching (faster data), image optimization with next/image priority hints, and removing unnecessary client-side state.

Migration Tips

1) Start with leaf components. Convert static UI to Server Components first. 2) Move data fetching up. Fetch in Server Components, pass data down. 3) Add 'use client' only when needed—interactivity, hooks, browser APIs. 4) Wrap slow sections in Suspense for streaming. 5) Audit your fetches—remove default caching where inappropriate.

Key Takeaway

App Router's performance benefits are real but not automatic. You get them by thinking about server vs client boundaries, parallelizing fetches, and streaming content. The framework enables performance—you still have to architect for it.