{
  "id": 9653710,
  "title": "What Broke When I Moved Client Projects to the Next.js App Router",
  "url": "https://urgent.news/2026/09/24/what-broke-when-i-moved-client-projects-to-the-next-js-app-router",
  "topic": "tech",
  "section": "Tech",
  "published": "2026-09-24T23:06:46.000Z",
  "source": {
    "name": "Dev.to",
    "slug": "dev-to",
    "url": "https://dev.to/abubakarfarooq/what-broke-when-i-moved-client-projects-to-the-nextjs-app-router-4g3d"
  },
  "original_language": "en",
  "account": "Upgrading client projects from the Pages Router to the Next.js App Router appeared to be a simple enhancement — until it turned out to be more complicated than anticipated. During all the migrations I've handled, the same set of issues tended to surface. Here are four migration challenges that resulted in significant time loss, and how they were resolved.\n\n1. getServerSideProps no longer exists. In the App Router, data fetching is handled through server components, which directly fetch data:\n\n```\nexport default async function Dashboard () {\nconst res = await fetch('https://api.example.com/stats', {\ncache: 'no-store', // opt out of default static caching\n});\nconst stats = await res.json();\nreturn <StatsGrid stats={stats} />;\n}\n```\n\nA common mistake was to overlook the default caching behavior in server components, which led to outdated data being displayed. When porting SSR pages, it's essential to verify your desired caching behavior.\n\n2. The 'use client' boundary moves frequently. By default, every component is now a server component in the App Router. The introduction of a client-only dependency — such as a chart library or a drag-and-drop widget — causes a build failure. The solution is to add the 'use client' directive as far down the tree as possible, allowing the rest of the page to still render on the server:\n\n```\nuse client\n\nimport { LineChart } from 'some-chart-library';\n\nexport function ChartCard({ data }) {\nreturn <LineChart data={data} />;\n}\n```\n\nIn one project, 'use client' was applied to an entire page, unexpectedly reversing the performance gains from the migration. Keep 'use client' at the leaf components level.\n\n3. The router API altered significantly. useRouter from next/router has been replaced by useRouter from next/navigation, while router.query is gone. Route parameters are accessed through useParams(), search parameters with useSearchParams(), and Head is now replaced by the metadata API:\n\n```\nexport const metadata = { title: 'Blog post', description: 'A migrated blog post' };\n\nexport default async function Post ({ params }) {\nconst { slug } = await params;\nconst post = await getPost(slug);\nreturn <article>{post.title}</article>;\n}\n```\n\nNote that in newer Next.js versions, params is a promise that must be awaited, which might cause hidden migration issues. Maintaining a checklist of router imports and replacing them before the initial build can prevent silent failures.\n\n4. Auth redirects require rethinking. Helpers from the Pages Router that depended on req / res do not correspond one-to-one with the App Router. Session checks must be moved into server components, and redirecting unauthenticated users is performed like this:\n\n```\nimport { redirect } from 'next/navigation';\n\nexport default async function AdminPage () {\nconst session = await getSession();\nif (!session) redirect('/login');\nreturn <AdminPanel user={session.user} />;\n}\n```\n\nIn conclusion, while the App Router migration proves beneficial — server components, streaming, and colocated data fetching genuinely simplify client projects — it should be treated as a comprehensive rewrite of your routing and data layer, not a simple find-and-replace operation. Migrate route by route, keep 'use client' boundaries small, and always verify caching behavior on every page you transfer. I create production Next.js apps and AI features for clients, and you can check out more of my work at theabubakar.dev.",
  "summary": "Moving client projects from the Pages Router to the Next.js App Router sounded like a routine upgrade — until it wasn't. Every migration I've done has surfaced the same handful of breakages. Here are the four that cost me real hours, and how I fix them now. 1. getServerSideProps doesn't exist anymore The first thing that breaks is data fetching. There is no getServerSideProps in the App Router —…",
  "key_points": [],
  "editors_take": null,
  "illustration": null,
  "coverage": {
    "outlets": 1,
    "also_reported_by": []
  },
  "ai_generated": true,
  "disclaimer": "Summaries, key points and the editor’s take are written by software from other outlets’ reporting and may contain errors — always check the linked original."
}