app-router
npx machina-cli add skill davepoon/buildwithclaude/app-router --openclawNext.js App Router Patterns
Overview
The App Router is Next.js's file-system based router built on React Server Components. It uses a app/ directory structure where folders define routes and special files control UI behavior.
Core File Conventions
Route Files
Each route segment is defined by a folder. Special files within folders control behavior:
| File | Purpose |
|---|---|
page.tsx | Unique UI for a route, makes route publicly accessible |
layout.tsx | Shared UI wrapper, preserves state across navigations |
loading.tsx | Loading UI using React Suspense |
error.tsx | Error boundary for route segment |
not-found.tsx | UI for 404 responses |
template.tsx | Like layout but re-renders on navigation |
default.tsx | Fallback for parallel routes |
Folder Conventions
| Pattern | Purpose | Example |
|---|---|---|
folder/ | Route segment | app/blog/ → /blog |
[folder]/ | Dynamic segment | app/blog/[slug]/ → /blog/:slug |
[...folder]/ | Catch-all segment | app/docs/[...slug]/ → /docs/* |
[[...folder]]/ | Optional catch-all | app/shop/[[...slug]]/ → /shop or /shop/* |
(folder)/ | Route group (no URL) | app/(marketing)/about/ → /about |
@folder/ | Named slot (parallel routes) | app/@modal/login/ |
_folder/ | Private folder (excluded) | app/_components/ |
Creating Routes
Basic Route Structure
To create a new route, add a folder with page.tsx:
app/
├── page.tsx # / (home)
├── about/
│ └── page.tsx # /about
└── blog/
├── page.tsx # /blog
└── [slug]/
└── page.tsx # /blog/:slug
Page Component
A page is a Server Component by default:
// app/about/page.tsx
export default function AboutPage() {
return (
<main>
<h1>About Us</h1>
<p>Welcome to our company.</p>
</main>
)
}
Dynamic Routes
Access route parameters via the params prop:
// app/blog/[slug]/page.tsx
interface PageProps {
params: Promise<{ slug: string }>
}
export default async function BlogPost({ params }: PageProps) {
const { slug } = await params
const post = await getPost(slug)
return <article>{post.content}</article>
}
Layouts
Root Layout (Required)
Every app needs a root layout with <html> and <body>:
// app/layout.tsx
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body>{children}</body>
</html>
)
}
Nested Layouts
Layouts wrap their children and preserve state:
// app/dashboard/layout.tsx
export default function DashboardLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<div className="flex">
<Sidebar />
<main className="flex-1">{children}</main>
</div>
)
}
Loading and Error States
Loading UI
Create instant loading states with Suspense:
// app/dashboard/loading.tsx
export default function Loading() {
return <div className="animate-pulse">Loading...</div>
}
Error Boundaries
Handle errors gracefully:
// app/dashboard/error.tsx
'use client'
export default function Error({
error,
reset,
}: {
error: Error
reset: () => void
}) {
return (
<div>
<h2>Something went wrong!</h2>
<button onClick={reset}>Try again</button>
</div>
)
}
Route Groups
Organize routes without affecting URL structure:
app/
├── (marketing)/
│ ├── layout.tsx # Marketing layout
│ ├── about/page.tsx # /about
│ └── contact/page.tsx # /contact
└── (shop)/
├── layout.tsx # Shop layout
└── products/page.tsx # /products
Metadata
Static Metadata
// app/about/page.tsx
import { Metadata } from 'next'
export const metadata: Metadata = {
title: 'About Us',
description: 'Learn more about our company',
}
Dynamic Metadata
// app/blog/[slug]/page.tsx
export async function generateMetadata({ params }: PageProps): Promise<Metadata> {
const { slug } = await params
const post = await getPost(slug)
return { title: post.title }
}
Key Patterns
- Colocation: Keep components, tests, and styles near routes
- Private folders: Use
_folderfor non-route files - Route groups: Use
(folder)to organize without URL impact - Parallel routes: Use
@slotfor complex layouts - Intercepting routes: Use
(.)patterns for modals
Resources
For detailed patterns, see:
references/routing-conventions.md- Complete file conventionsreferences/layouts-templates.md- Layout composition patternsreferences/loading-error-states.md- Suspense and error handlingexamples/dynamic-routes.md- Dynamic routing examplesexamples/parallel-routes.md- Parallel and intercepting routes
Source
git clone https://github.com/davepoon/buildwithclaude/blob/main/plugins/nextjs-expert/skills/app-router/SKILL.mdView on GitHub Overview
Learn how Next.js App Router structures routes with the app/ directory and per-route UI controls. It covers core file conventions (page.tsx, layout.tsx, loading.tsx, error.tsx, not-found.tsx, template.tsx, default.tsx) and routing patterns like dynamic, catch-all, and route groups.
How This Skill Works
Routes are defined by folder segments under app/. Each segment uses specific files to control UI and behavior; Next.js renders UI via Server Components by default. Use dynamic segments like [slug], catch-all [...slug], and optional [[...slug]] to match routes, while route groups with (folder) or parallel routes via @folder enable complex layouts.
When to Use It
- When creating a new route under the app/ directory with page.tsx
- When you need a shared layout that preserves state across navigations
- When you want per-route loading states or error boundaries
- When implementing dynamic routes with parameters or catch-all patterns
- When organizing routes with route groups or parallel routes
Quick Start
- Step 1: Create app/ with a page.tsx for a route (e.g., app/about/page.tsx)
- Step 2: Add a root or nested layout (e.g., app/layout.tsx or app/dashboard/layout.tsx)
- Step 3: Enhance UX with loading.tsx and error.tsx for resilience
Best Practices
- Define a root layout at app/layout.tsx to wrap all pages
- Use layout.tsx for shared UI and state preservation across children
- Leverage page.tsx for route UI (server component by default)
- Add loading.tsx and error.tsx to handle loading and errors per route
- Use dynamic segments [slug], catch-all [...slug], and optional [[...slug]] as needed
Example Use Cases
- Create a blog: app/blog/page.tsx for /blog and app/blog/[slug]/page.tsx for /blog/:slug
- Marketing site organized with route groups like app/(marketing)/about/page.tsx
- Dashboard with a root layout and nested layouts in app/dashboard/layout.tsx
- Loading state implemented in app/dashboard/loading.tsx
- Per-route error boundary in app/dashboard/error.tsx