The App Router has been stable for a while now, but most teams we talk to are still using it like the old Pages Router with a different folder structure. That leaves a lot of value on the table. Here are the patterns that have earned a permanent place in our starter templates.
Nested layouts for shared UI, not just shared chrome
The obvious use of a layout.tsx file is a persistent navbar and footer. The pattern we underused at first: nesting layouts specifically to share data-fetching, not just markup. A /dashboard/[team]/layout.tsx that fetches team settings once means every page beneath it — billing, members, integrations — gets that data for free without re-fetching or prop drilling.
// app/dashboard/[team]/layout.tsx
export default async function TeamLayout({ children, params }) {
const team = await getTeam(params.team);
return (
<TeamProvider team={team}>
{children}
</TeamProvider>
);
}
Parallel routes for dashboards that feel instant
Parallel routes let you render multiple independent segments in the same layout, each with its own loading and error state. For a dashboard with a chart, a table, and an activity feed, that means the chart can render the moment its data resolves instead of waiting on the slowest of the three requests. The perceived speed improvement is bigger than any bundle-size optimization we've shipped.
Error boundaries at the segment level, not just the root
A single error.tsx at the root of your app is better than nothing, but it means one failed API call takes down the entire page. Placing error.tsx files at each route segment means a broken "recent activity" widget degrades gracefully instead of blanking the whole dashboard.
Key takeaway
Treat every folder in the App Router as an opportunity to scope loading states, error boundaries, and data fetching — not just routes. The router rewards granularity.
What we've learned to avoid
- Over-nesting layouts. Five levels of nested layouts for a simple marketing site adds indirection without benefit. Match the nesting to genuinely shared concerns.
- Fetching in client components when a server component would do. It's tempting to reach for familiar
useEffectpatterns. Resist it for anything that doesn't need interactivity. - Ignoring route groups. Wrapping unrelated routes in
(marketing)and(app)groups keeps layouts and middleware logic from leaking between very different parts of a product.
None of these patterns are exotic — they're in the official docs. The difference is treating them as defaults rather than advanced options you reach for only when something breaks.