Claude
Skills
Sign in
Back

nextjs-app-router-fundamentals

Included with Lifetime
$97 forever

Guide for working with Next.js App Router (Next.js 13+). Use when migrating from Pages Router to App Router, creating layouts, implementing routing, handling metadata, or building Next.js 13+ applications. Activates for App Router migration, layout creation, routing patterns, or Next.js 13+ development tasks.

Web Dev

What this skill does


# Next.js App Router Fundamentals

## Overview

Provide comprehensive guidance for Next.js App Router (Next.js 13+), covering migration from Pages Router, file-based routing conventions, layouts, metadata handling, and modern Next.js patterns.

## TypeScript: NEVER Use `any` Type

**CRITICAL RULE:** This codebase has `@typescript-eslint/no-explicit-any` enabled. Using `any` will cause build failures.

**❌ WRONG:**
```typescript
function handleSubmit(e: any) { ... }
const data: any[] = [];
```

**✅ CORRECT:**
```typescript
function handleSubmit(e: React.FormEvent<HTMLFormElement>) { ... }
const data: string[] = [];
```

### Common Next.js Type Patterns

```typescript
// Page props
function Page({ params }: { params: { slug: string } }) { ... }
function Page({ searchParams }: { searchParams: { [key: string]: string | string[] | undefined } }) { ... }

// Form events
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => { ... }
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => { ... }

// Server actions
async function myAction(formData: FormData) { ... }
```

## When to Use This Skill

Use this skill when:
- Migrating from Pages Router (`pages/` directory) to App Router (`app/` directory)
- Creating Next.js 13+ applications from scratch
- Working with layouts, templates, and nested routing
- Implementing metadata and SEO optimizations
- Building with App Router routing conventions
- Handling route groups, parallel routes, or intercepting routes basics

## Core Concepts

### App Router vs Pages Router

**Pages Router (Legacy - Next.js 12 and earlier):**
```
pages/
├── index.tsx              # Route: /
├── about.tsx              # Route: /about
├── _app.tsx               # Custom App component
├── _document.tsx          # Custom Document component
└── api/                   # API routes
    └── hello.ts           # API endpoint: /api/hello
```

**App Router (Modern - Next.js 13+):**
```
app/
├── layout.tsx             # Root layout (required)
├── page.tsx               # Route: /
├── about/                 # Route: /about
│   └── page.tsx
├── blog/
│   ├── layout.tsx         # Nested layout
│   └── [slug]/
│       └── page.tsx       # Dynamic route: /blog/:slug
└── api/                   # Route handlers
    └── hello/
        └── route.ts       # API endpoint: /api/hello
```

### File Conventions

**Special Files in App Router:**
- `layout.tsx` - Shared UI for a segment and its children (preserves state, doesn't re-render)
- `page.tsx` - Unique UI for a route, makes route publicly accessible
- `loading.tsx` - Loading UI with React Suspense
- `error.tsx` - Error UI with Error Boundaries
- `not-found.tsx` - 404 UI
- `template.tsx` - Similar to layout but re-renders on navigation
- `route.ts` - API endpoints (Route Handlers)

**Colocation:**
- Components, tests, and other files can be colocated in `app/`
- Only `page.tsx` and `route.ts` files create public routes
- Other files (components, utils, tests) are NOT routable

## Migration Guide: Pages Router to App Router

### Step 1: Understand the Current Structure

Examine existing Pages Router setup:
- Read `pages/` directory structure
- Identify `_app.tsx` - handles global state, layouts, providers
- Identify `_document.tsx` - customizes HTML structure
- Note metadata usage (`next/head`, `<Head>` component)
- List all routes and dynamic segments

### Step 2: Create Root Layout

Create `app/layout.tsx` - **REQUIRED** for all App Router applications:

```typescript
// app/layout.tsx
export const metadata = {
  title: 'My App',
  description: 'App description',
};

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <body>{children}</body>
    </html>
  );
}
```

**Migration Notes:**
- Move `_document.tsx` HTML structure to `layout.tsx`
- Move `_app.tsx` global providers/wrappers to `layout.tsx`
- Convert `<Head>` metadata to `metadata` export
- The root layout **MUST** include `<html>` and `<body>` tags

### Step 3: Migrate Pages to Routes

**Simple Page Migration:**
```typescript
// Before: pages/index.tsx
import Head from 'next/head';

export default function Home() {
  return (
    <>
      <Head>
        <title>Home Page</title>
      </Head>
      <main>
        <h1>Welcome</h1>
      </main>
    </>
  );
}
```

```typescript
// After: app/page.tsx
export default function Home() {
  return (
    <main>
      <h1>Welcome</h1>
    </main>
  );
}

// Metadata moved to layout.tsx or exported here
export const metadata = {
  title: 'Home Page',
};
```

**Nested Route Migration:**
```typescript
// Before: pages/blog/[slug].tsx
export default function BlogPost() { ... }
```

```typescript
// After: app/blog/[slug]/page.tsx
export default function BlogPost() { ... }
```

### Step 4: Update Navigation

Replace anchor tags with Next.js Link:

```typescript
// Before (incorrect in App Router)
<a href="/about">About</a>

// After (correct)
import Link from 'next/link';
<Link href="/about">About</Link>
```

### Step 5: Clean Up Pages Directory

After migration:
- Remove all page files from `pages/` directory
- Keep `pages/api/` if you're not migrating API routes yet
- Remove `_app.tsx` and `_document.tsx` (functionality moved to layout)
- Optionally delete empty `pages/` directory

## Metadata Handling

### Static Metadata

```typescript
// app/page.tsx or app/layout.tsx
import type { Metadata } from 'next';

export const metadata: Metadata = {
  title: 'My Page',
  description: 'Page description',
  keywords: ['nextjs', 'react'],
  openGraph: {
    title: 'My Page',
    description: 'Page description',
    images: ['/og-image.jpg'],
  },
};
```

### Dynamic Metadata

```typescript
// app/blog/[slug]/page.tsx
export async function generateMetadata({
  params
}: {
  params: { slug: string }
}): Promise<Metadata> {
  const post = await getPost(params.slug);

  return {
    title: post.title,
    description: post.excerpt,
  };
}
```

## Layouts and Nesting

### Creating Nested Layouts

```typescript
// app/layout.tsx - Root layout
export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        <Header />
        {children}
        <Footer />
      </body>
    </html>
  );
}

// app/blog/layout.tsx - Blog layout
export default function BlogLayout({ children }) {
  return (
    <div>
      <BlogSidebar />
      <main>{children}</main>
    </div>
  );
}
```

**Layout Behavior:**
- Layouts preserve state across navigation
- Layouts don't re-render on route changes
- Parent layouts wrap child layouts
- Root layout is required and wraps entire app

## Routing Patterns

### Dynamic Routes

```typescript
// app/blog/[slug]/page.tsx
export default function BlogPost({
  params
}: {
  params: { slug: string }
}) {
  return <article>Post: {params.slug}</article>;
}
```

### Catch-All Routes

```typescript
// app/shop/[...slug]/page.tsx - Matches /shop/a, /shop/a/b, etc.
export default function Shop({
  params
}: {
  params: { slug: string[] }
}) {
  return <div>Path: {params.slug.join('/')}</div>;
}
```

### Optional Catch-All

```typescript
// app/shop/[[...slug]]/page.tsx - Matches /shop AND /shop/a, /shop/a/b
```

### Route Groups

Group routes without affecting URL:

```
app/
├── (marketing)/
│   ├── about/
│   │   └── page.tsx      # /about
│   └── contact/
│       └── page.tsx      # /contact
└── (shop)/
    └── products/
        └── page.tsx      # /products
```

## Common Migration Pitfalls

### Pitfall 1: Forgetting Root Layout HTML Tags

**Wrong:**
```typescript
export default function RootLayout({ children }) {
  return <div>{children}</div>; // Missing <html> and <body>
}
```

**Correct:**
```typescript
export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body>{children}</body>
    </html>
  );
}
```

### Pitfall 2: Using `next/head` in App Router

**Wrong:**
```typescript
import Head from 'next/head';

export default function Page() {
  return (
    <>
 

Related in Web Dev