Wasi Naseer

Notes on the Next.js App Router after shipping it to production

Things I wish I'd known before migrating a real app from Pages Router to the App Router.

2 min read
Table of contents

I finished a migration from the Pages Router to the App Router last month. Here are the notes I'd give my past self.

Server components are the point

If you treat the App Router as "Pages Router with new file names," you'll have a bad time. The whole model only pays off once you lean into server components by default and push "use client" as far down the tree as you can. Data fetching moves to the component that needs it. Props stop being a waterfall.

generateStaticParams is your friend

For content-heavy sites, generateStaticParams gives you the same SSG guarantees as the old getStaticPaths, with less ceremony:

export function generateStaticParams() {
  return getAllPostSlugs().map((slug) => ({ slug }));
}

Combine it with generateMetadata and you get fully static, SEO-friendly pages without any of the old getStaticProps plumbing.

Metadata API: use it

Don't hand-roll <head> tags. The metadata API handles titles, descriptions, Open Graph, Twitter cards, canonicals, and the rest with full type safety.

Template titles

Setting a template in the root layout means every page gets a branded title for free:

title: {
  default: siteConfig.title,
  template: `%s — ${siteConfig.name}`,
}

Things that bit me

  • searchParams and params are now promises in newer versions — await them in server components.
  • Client components can't import server-only modules. If you accidentally pull in fs through a shared util, the error message is unhelpful.
  • next/image in MDX needs you to map the img component explicitly.

Would I do it again

Yes. The mental model is cleaner once it clicks, and the defaults push you toward better performance.