Get the FREE Ultimate OpenClaw Setup Guide →

app-router

npx machina-cli add skill davepoon/buildwithclaude/app-router --openclaw
Files (1)
SKILL.md
5.2 KB

Next.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:

FilePurpose
page.tsxUnique UI for a route, makes route publicly accessible
layout.tsxShared UI wrapper, preserves state across navigations
loading.tsxLoading UI using React Suspense
error.tsxError boundary for route segment
not-found.tsxUI for 404 responses
template.tsxLike layout but re-renders on navigation
default.tsxFallback for parallel routes

Folder Conventions

PatternPurposeExample
folder/Route segmentapp/blog//blog
[folder]/Dynamic segmentapp/blog/[slug]//blog/:slug
[...folder]/Catch-all segmentapp/docs/[...slug]//docs/*
[[...folder]]/Optional catch-allapp/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

  1. Colocation: Keep components, tests, and styles near routes
  2. Private folders: Use _folder for non-route files
  3. Route groups: Use (folder) to organize without URL impact
  4. Parallel routes: Use @slot for complex layouts
  5. Intercepting routes: Use (.) patterns for modals

Resources

For detailed patterns, see:

  • references/routing-conventions.md - Complete file conventions
  • references/layouts-templates.md - Layout composition patterns
  • references/loading-error-states.md - Suspense and error handling
  • examples/dynamic-routes.md - Dynamic routing examples
  • examples/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

  1. Step 1: Create app/ with a page.tsx for a route (e.g., app/about/page.tsx)
  2. Step 2: Add a root or nested layout (e.g., app/layout.tsx or app/dashboard/layout.tsx)
  3. 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

Frequently Asked Questions

Add this skill to your agents
Sponsor this space

Reach thousands of developers