The evolution of our stack
We re-evaluate our default framework once a year, on purpose, so the choice stays a decision rather than a habit. When the App Router stabilised it became our default for enterprise work, and a year of projects has not changed our mind.
Server components changed the data layer
The interesting part was never the rendering. It was that data fetching moved next to the component that needs the data, without shipping the fetching code to the browser.
Performance. Our marketplace build saw a 35% improvement in Largest Contentful Paint after the move, almost all of it from JavaScript that stopped being sent.
Developer experience. Layouts, loading states and error boundaries as files means less plumbing per route and fewer conventions to explain to a new engineer.
SEO. The metadata API and generated sitemaps removed a whole class of "the page is fine but Google cannot see it" tickets.
The architecture we settled on
- Route files stay thin: metadata, params and the data fetch.
- Feature UI lives in a community folder, one folder per domain.
- Services own every call to the database and never throw at the page.
- TypeScript interfaces sit between the two and are the contract.
Where it still bites
The server-to-client boundary is real and unforgiving about what can cross it. Functions cannot be props. A module imported through a 'use client' barrel becomes a client reference whether you meant it or not — including, memorably, a plain array of data we spent an afternoon tracking down.
Write the boundary down somewhere your team reads. That single page of notes has saved us more time than any optimisation in this post.
Part of the team building and running the products behind these posts at Hedaya Global Solutions.