About app-router
A reference skill for routing patterns in the Next.js App Router. It helps developers structure routes and layouts using the app directory, including dynamic routes and loading or error states.
Key features
- 01Route and special-file conventions
- 02Dynamic and catch-all routes
- 03Nested layouts and route groups
- 04Loading and error states
- 05Static and dynamic metadata
Use cases
- 01Structuring routes in a Next.js app
- 02Organizing layouts without changing URLs
- 03Building pages for dynamic route parameters
- 04Adding loading and error UI
Install
Copy the command and run it in a terminal.
npx skills add https:/ / github.com/ davepoon/ buildwithclaude --skill app-routerAfter installing, type/app-routerin Claude Code to confirm it loaded.
Bundled files
References and scripts installed along with SKILL.md.
examples/dynamic-routes.mdparallel-routes.mdreferences/layouts-templates.mdloading-error-states.mdrouting-conventions.mdDetails
From the project READMENext.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