✳ Nairobi, Kenya
- WhatsApp+254 799 756 331
- Emailhello@muvevi.com
- LinkedInlinkedin.com/in/muvevi
- GitHubgithub.com/mutua-muvevi
- Twitter / X@muvevi
Available for freelance & full-time roles.
Let's build something great.
Next.js App Router patterns I use on every project
Parallel routes, intercepted routes, server actions — the App Router is powerful but the docs bury the patterns. A practical guide to what I actually use.
The App Router introduced a lot of primitives at once and the documentation covers them individually but rarely shows how they compose. Here's the patterns I reach for on every project.
Route groups for layout splitting. Wrapping routes in (group) directories lets you have multiple root layouts without affecting the URL. Every project I ship has at least two: (public) for the marketing site and (dashboard) for the app. They share a root layout for fonts and globals, but have completely different shell layouts — the marketing site has no sidebar; the app has no footer.
app/
├── layout.tsx ← fonts, globals, providers
├── (public)/
│ ├── layout.tsx ← Navbar, SiteFooter
│ └── page.tsx ← homepage
└── (dashboard)/
├── layout.tsx ← Sidebar, Topbar
└── os/page.tsx ← dashboard rootParallel routes for modals. The classic "open a modal without navigating away but keep the URL" use case. You create a @modal slot alongside the page, and the modal renders at the current URL. The background page stays mounted. Closing the modal navigates back with router.back(). This is the right way to do product image lightboxes, edit-in-place flows, and confirmation dialogs that have shareable URLs.
Server actions for mutations. Forget client-side fetch wrappers for simple mutations. A server action is a function marked "use server" — it runs on the server and you call it directly from a form or a button handler. No API route needed, no separate HTTP request type. For small mutations (toggle a flag, update a name field), this is less code and the form progressively enhances without JavaScript.
Route handlers for structured APIs. For anything that needs to be callable from outside the app (webhooks, mobile client, third-party integrations), Route Handlers still make sense. But I've stopped using them for internal UI data fetching — that's what async server components are for.
Streaming with Suspense. Any async server component automatically streams. Wrapping a slow component in <Suspense fallback={<Skeleton />}> means the page shell arrives instantly, the slow data loads in the background, and React patches it in. For dashboard pages with multiple independent data sources, this means the page feels fast even when some queries are slow.
The pattern I reach for on heavy pages: wrap each data-heavy section in its own <Suspense>, give each a skeleton that matches the section's dimensions, and let them load in parallel. The user sees content progressively rather than waiting for the slowest query.