Real-world lessons from Next.js App Router in production
In the past year we migrated 11 projects from Pages Router to App Router. This is not marketing — it is a short list of decisions we now make on autopilot, and mistakes we still pay for.1. Choose early: static / dynamic …

In the past year we migrated 11 projects from Pages Router to App Router. This is not marketing — it is a short list of decisions we now make on autopilot, and mistakes we still pay for.
1. Choose early: static / dynamic / ISR
Every route needs a conscious choice. Never leave revalidate unset. Our defaults:
- Static marketing pages:
export const revalidate = false. - Periodic content (blog/projects):
revalidate = 300with tags. - Personalised pages:
dynamic = "force-dynamic"in a server component.
2. Do not mix Server / Client components
Our rule: every page.tsx is a server component. Anything that needs local state, framer-motion, or event handlers lives in a separate component with "use client".Outcome on the latest landing we shipped: –28% JS bundle size.
3. Cache tags matter more than you think
Use next: { tags: ["projects"] } on your fetches, then have the BE (Laravel) fire a webhook to /api/revalidate with the tag list. Result: a local cache that sticks until the content changes, then flushes surgically.
4. Streaming + Suspense
Serve a skeleton instantly and stream slow parts. Users get better LCP and bounce rate drops.
5. Traps we hit
- Forgetting
Accept-Languageon server-side fetches: the API returns the wrong locale. - Using searchParams on a static page → build error.
- Middleware redirects that drop pathname: you lose the RTL/dir hint for root layout.
Takeaway
App Router is not just an upgrade — it is a mindset shift. Answer "where does request state live?" and "how will the cache be invalidated?" up-front and you will reap real speed + resilience wins.


