Expert guide for building applications with Next.js App Router, covering file-based routing, layouts, pages, dynamic routes, and navigation patterns.
You are an expert in Next.js App Router patterns and file-based routing. Help developers build modern Next.js applications following best practices.
Next.js uses file-system based routing where folders and files define routes:
A page is UI rendered on a specific route. Create pages by adding a `page.tsx` file in the app directory:
```typescript
// app/page.tsx - Root page (/)
export default function Page() {
return <h1>Hello Next.js!</h1>
}
// app/blog/page.tsx - Blog page (/blog)
export default async function BlogPage() {
const posts = await getPosts()
return (
<ul>
{posts.map((post) => (
<Post key={post.id} post={post} />
))}
</ul>
)
}
```
Layouts are UI shared between multiple pages. They preserve state, remain interactive, and don't rerender on navigation.
```typescript
// app/layout.tsx - Root layout (required)
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body>
<main>{children}</main>
</body>
</html>
)
}
// app/blog/layout.tsx - Nested layout for /blog routes
export default function BlogLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<section>
<nav>{/* Blog navigation */}</nav>
{children}
</section>
)
}
```
**Important:** The root layout must contain `<html>` and `<body>` tags.
Create dynamic route segments by wrapping folder names in square brackets:
```typescript
// app/blog/[slug]/page.tsx - Dynamic blog post route
export default async function BlogPostPage({
params,
}: {
params: Promise<{ slug: string }>
}) {
const { slug } = await params
const post = await getPost(slug)
return (
<div>
<h1>{post.title}</h1>
<p>{post.content}</p>
</div>
)
}
```
Dynamic segments can be accessed via the `params` prop, which is a Promise that must be awaited.
Access search parameters in Server Components using the `searchParams` prop:
```typescript
// app/page.tsx
export default async function Page({
searchParams,
}: {
searchParams: Promise<{ [key: string]: string | string[] | undefined }>
}) {
const filters = (await searchParams).filters
// Use filters for pagination, filtering, etc.
}
```
**Note:** Using `searchParams` opts the page into dynamic rendering.
For Client Components, use the `useSearchParams` hook:
```typescript
'use client'
import { useSearchParams } from 'next/navigation'
export default function SearchBar() {
const searchParams = useSearchParams()
const query = searchParams.get('q')
}
```
Use the `<Link>` component for client-side navigation with automatic prefetching:
```typescript
import Link from 'next/link'
export default function PostList({ posts }) {
return (
<ul>
{posts.map((post) => (
<li key={post.slug}>
<Link href={`/blog/${post.slug}`}>{post.title}</Link>
</li>
))}
</ul>
)
}
```
For programmatic navigation:
```typescript
'use client'
import { useRouter } from 'next/navigation'
export default function LoginButton() {
const router = useRouter()
const handleLogin = async () => {
await login()
router.push('/dashboard')
}
return <button onClick={handleLogin}>Login</button>
}
```
Key special files in the App Router:
Organize routes without affecting URL structure using parentheses:
```
app/
(marketing)/
about/page.tsx → /about
contact/page.tsx → /contact
(shop)/
products/page.tsx → /products
cart/page.tsx → /cart
```
When helping developers implement Next.js App Router patterns:
1. **Always create a root layout** with `<html>` and `<body>` tags
2. **Use Server Components by default** - only add `'use client'` when needed for interactivity, hooks, or browser APIs
3. **Await params and searchParams** - these are Promises in Next.js 15+
4. **Colocate components** - place related components near the routes that use them
5. **Use dynamic segments** for data-driven routes (blog posts, products, etc.)
6. **Nest layouts** for shared UI in specific route segments
7. **Use `<Link>`** for navigation instead of `<a>` tags
8. **Implement loading and error states** using `loading.tsx` and `error.tsx`
9. **Keep searchParams usage intentional** - it opts pages into dynamic rendering
10. **Use route groups** to organize code without affecting URLs
```typescript
// app/blog/[slug]/page.tsx
async function getPost(slug: string) {
const res = await fetch(`https://api.example.com/posts/${slug}`, {
next: { revalidate: 3600 } // Cache for 1 hour
})
return res.json()
}
export default async function Page({ params }: { params: Promise<{ slug: string }> }) {
const { slug } = await params
const post = await getPost(slug)
return <Article post={post} />
}
```
```typescript
// Generate static paths at build time
export async function generateStaticParams() {
const posts = await getPosts()
return posts.map((post) => ({
slug: post.slug,
}))
}
```
```typescript
import type { Metadata } from 'next'
export async function generateMetadata({
params,
}: {
params: Promise<{ slug: string }>
}): Promise<Metadata> {
const { slug } = await params
const post = await getPost(slug)
return {
title: post.title,
description: post.excerpt,
}
}
```
Use this skill when developers need help with:
Always reference the official Next.js documentation at https://nextjs.org/docs/app for the latest updates and patterns.
Leave a review
No reviews yet. Be the first to review this skill!
# Download SKILL.md from killerskills.ai/api/skills/nextjs-app-router/raw