Engineering
Next.js App Router: What We Are Adopting Now and What Can Wait
Since Next.js 13 landed in October, every kickoff call with a technical stakeholder includes the same question: pages directory or the new app directory? It is still marked beta, the ecosystem is mid-migration, and the mental model is genuinely different. We have now shipped one marketing site and one internal tool on the App Router, and prototyped a third project before deliberately retreating to pages. Here is where we have landed, as of January 2023.
The mental model is the actual migration
The file conventions are easy. The hard part is that React Server Components invert a habit every React developer has had since 2015: components are now server-first, and interactivity is what you opt into. Once that clicks, a lot of ceremony disappears. Data fetching happens where the data is used, not hoisted into getServerSideProps and threaded down through props.
// app/work/page.tsx
export default async function WorkPage() {
const projects = await getProjects(); // runs on the server, ships no JS
return <ProjectGrid projects={projects} />;
}
No loader boilerplate, no client-side fetching waterfall, and the component above contributes zero JavaScript to the bundle. For content-heavy marketing sites, which is a large share of what a studio like ours builds, this is the right architecture and we are adopting it now.
What we are using in production today
- Layouts. Nested layouts that do not re-render on navigation are worth the migration by themselves. Persistent nav state used to require ugly workarounds.
- Server Components for anything static. Cards, footers, article bodies. The bundle savings on our last build were real: roughly 30 percent less client JS than the pages-directory equivalent.
- Route groups for separating marketing and app shells without contorting the URL structure.
What we are deliberately waiting on
- Complex mutation flows. Server Actions are alpha and we are not shipping alpha primitives to clients. For forms we still use route handlers or a classic API route, and it is fine.
- Anything needing mature ecosystem support. Several libraries we rely on still assume client rendering everywhere. Check your dependencies before you commit; "use client" at the top of half your tree defeats the purpose.
- Heavy client-state applications. For dashboard-style products where nearly everything is interactive anyway, the benefits shrink and the beta rough edges remain. Our prototype retreat was one of these.
Caching will bite you exactly once
The new fetch-level caching defaults to aggressive. Our staging environment served a stale CMS entry for a confusing afternoon before we internalized revalidation. Decide per fetch, explicitly, whether the data is static, revalidated on an interval, or dynamic. Write it down in the code review checklist. The defaults are opinionated and the opinions are not always yours.
Our recommendation, dated for the record
For new marketing and content sites starting this quarter: App Router, yes, with eyes open about beta status. For products with heavy interactivity or a hard deadline: pages directory remains completely respectable, and migration later is incremental since both routers can coexist in one app.
The direction is not in doubt. Server-first React is where the framework, and honestly the platform, is heading. But there is a difference between betting on a direction and volunteering your client as a beta tester. Know which one you are doing, and say so out loud in the kickoff.
Building something this could apply to?
We take on a small number of flagship projects each quarter.
Start a project