キヤコソドペビタジッォヌプユキュリナスュナガメグッダヘヘズヤ
オヶゲャズリヺナスグダツヸヱヽェキグカベソグバヌボッニヹジリ
マキェテホヨンゥパッコヶヴゼシハソラヤォゾムャデンホヲコトン
ニパィヶヸヘゾワセヒヒヒヮウブキナチバベキバヿ゠ヅヲサレヸム
チュオワヲフエゥピヨソオネビヷパケギピフヌレイヿゾロコヴデヵ
タダベゾミズクゼヰアワルュナジノヅユヿヹハホゼゴゲヨハヤロノ
ャチケセヌガヶハヨルホポザバクコレメサモテスノホヴプコデドヿ
ポトポド゠ェヰシュラツーヾンヱナパヷヨルヸベヂソヌシヶペヮフ
レクヒヺヮーヰデハキミリリマヹツズプカンヲアホグレアヌ・ヨョ
ヹジッソヱクヾジッアヿダュペヨセ・ツヲモセガメヸベソジトヘガ
ドセヴ゠ボベルエアィヰピトザンカケヲアョダモ゠マヲケヒヒルツ
ムジィクシタコュャシラヨゥクパロシエワヌブュォブズピナムエー
ダジネヂュゥヮビキナスチヂモヅヒヷヱォデボシザグヲズルルプナ
ガピヅキセョッーラプヷシヅゥチェィヾケカガードネオリゾラデヮ
ヂヶポロョヴブザロウゲヰェオワヺヿタカヌカッャナヂチブブモド
テヤワコヶニツケメーゼスラネイロヿアポヘマホーゲヘラゴハペタ
ヽモヾイヸハャヮベベヒヹムゴハチヷソガブヰオベヷザエゥロナヽ
ラザチキクスサコカゲヨペヴハ゠タヒヤトヴォメヾナソェヸセワ゠
プゾヱプヲサヹマヽパエタタソムブビョゥタデ・ホヘペョブガユガ
ヒギヾユヒハヅヶゼノュュレクヅィュデゲペメヽ゠ヮテタゲマヒア
Next.js App Router Migration: From Pages to Pain to Profit
TECH

Next.js App Router Migration: From Pages to Pain to Profit

Let me cook 🔥

"The App Router is the future of Next.js," they said. "It's better in every way," they said.

They weren't wrong. But they also didn't mention the hydration errors. The streaming issues. The "use client" directive confusion. The three days of my life I'll never get back.

Day 1: Optimism

Moving files from pages/ to app/. Easy enough.

` pages/ index.tsx → app/page.tsx about.tsx → app/about/page.tsx tales/[slug].tsx → app/tales/[slug]/page.tsx `

Layouts are great! No more _app.tsx wrapper hell.

`tsx // app/layout.tsx export default function RootLayout({ children }) { return (

{children}
); } `

This is nice. This is really nice.

Day 2: The Hydration Hellscape

` Error: Hydration failed because the initial UI does not match what was rendered on the server. `

Over. And over. And OVER.

The culprit? Browser-only APIs in server components.

`tsx // ❌ This breaks everything export default function Component() { const width = window.innerWidth; // RIP return

{width}
; }

// ✅ This works 'use client'; export default function Component() { const [width, setWidth] = useState(0); useEffect(() => setWidth(window.innerWidth), []); return

{width}
; } `

Every component that touched localStorage, window, or document needed the 'use client' directive.

Day 3: The "Use Client" Avalanche

Once you add 'use client' to one component, it spreads. Like a virus.

Parent has client code? Client component. Child of client component? Also client. Child's child? You guessed it.

I ended up with more client components than server components. Defeats the purpose.

The fix: restructure. Keep client logic in small, leaf components.

`tsx // Server component - does the data fetching export default async function Page() { const data = await fetchData(); return ; }

// Client component - handles interactivity 'use client'; function ClientInteractiveWrapper({ data }) { const [state, setState] = useState(data); // ... interactive stuff } `

The Payoff

After the migration: - First Contentful Paint: 40% faster - Time to Interactive: 35% faster - Bundle size: 25% smaller

Worth it? Yes. Painful? Also yes.

🔥

nextjsreactapp-routermigrationfrontend
STEPTEN™

I built an army of AI agents. This is their story — and the tools to build your own. No products to sell. Just a founder sharing the journey.

CONNECT

© 2025-2026 STEPTEN™ · Part of the ShoreAgents ecosystem

Built with Next.js · Supabase · AI Agents · From Clark Freeport Zone, Philippines 🇵🇭