This skill should be used when the user asks to "create a Next.js route", "add a page", "set up layouts", "implement loading states", "add error boundaries", "organize routes", "create dynamic...
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.
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 |
| 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/ |
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
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>
)
}
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>
}
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>
)
}
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>
)
}
Create instant loading states with Suspense:
// app/dashboard/loading.tsx
export default function Loading() {
return <div className="animate-pulse">Loading...</div>
}
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>
)
}
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
// app/about/page.tsx
import { Metadata } from 'next'
export const metadata: Metadata = {
title: 'About Us',
description: 'Learn more about our company',
}
// 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 }
}
_folder for non-route files(folder) to organize without URL impact@slot for complex layouts(.) patterns for modalsFor 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