App Router
4 snippetsFile-based routing conventions
Page
// app/about/page.tsx
export default function AboutPage() {
return <h1>About</h1>;
} Layout
// app/layout.tsx
export default function RootLayout({ children }) {
return (
<html><body>{children}</body></html>
);
} Loading & Error
// app/loading.tsx
export default function Loading() {
return <div>Loading...</div>;
}
// app/error.tsx
'use client';
export default function Error({ error, reset }) {
return <button onClick={reset}>Retry</button>;
} Dynamic Route
// app/posts/[slug]/page.tsx
export default function Post({ params }) {
return <h1>{params.slug}</h1>;
} Server Components
3 snippetsAsync data fetching on the server
Async Component
// Server component (default)
export default async function Users() {
const users = await db.user.findMany();
return <ul>{users.map(u => <li key={u.id}>{u.name}</li>)}</ul>;
} Server Action
// app/actions.ts
'use server';
export async function createPost(formData: FormData) {
const title = formData.get('title');
await db.post.create({ data: { title } });
revalidatePath('/posts');
} generateStaticParams
export async function generateStaticParams() {
const posts = await db.post.findMany();
return posts.map((post) => ({ slug: post.slug }));
} Client Components
2 snippetsInteractive UI with hooks
use client
'use client';
import { useState } from 'react';
export default function Counter() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(c => c + 1)}>{count}</button>;
} Form with Action
'use client';
import { createPost } from './actions';
export default function Form() {
return (
<form action={createPost}>
<input name="title" />
<button type="submit">Create</button>
</form>
);
} Tired of looking up syntax?
DocuWriter.ai generates documentation and explains code using AI.
Data Fetching
3 snippetsCaching and revalidation
fetch + cache
// Cached by default (static)
const data = await fetch('https://api.example.com');
// Revalidate every 60s
const data = await fetch(url, { next: { revalidate: 60 } });
// No cache
const data = await fetch(url, { cache: 'no-store' }); revalidatePath
import { revalidatePath, revalidateTag } from 'next/cache';
revalidatePath('/posts'); // Revalidate page
revalidateTag('posts'); // Revalidate by tag Route Handlers
// app/api/users/route.ts
import { NextResponse } from 'next/server';
export async function GET() {
const users = await db.user.findMany();
return NextResponse.json(users);
}
export async function POST(request: Request) {
const body = await request.json();
return NextResponse.json(body, { status: 201 });
} Metadata & SEO
3 snippetsPage metadata and Open Graph
Static Metadata
export const metadata = {
title: 'My Site',
description: 'Welcome',
openGraph: { title: 'My Site', images: ['/og.png'] },
}; Dynamic Metadata
export async function generateMetadata({ params }) {
const post = await getPost(params.slug);
return { title: post.title, description: post.excerpt };
} Sitemap
// app/sitemap.ts
export default async function sitemap() {
const posts = await db.post.findMany();
return posts.map((post) => ({
url: 'https://example.com/posts/' + post.slug,
lastModified: post.updatedAt,
}));
} Middleware
2 snippetsRequest interception
Basic Middleware
// middleware.ts
import { NextResponse } from 'next/server';
export function middleware(request) {
if (!request.cookies.get('token')) {
return NextResponse.redirect(new URL('/login', request.url));
}
return NextResponse.next();
}
export const config = { matcher: ['/dashboard/:path*'] }; Headers & Rewrites
export function middleware(request) {
const response = NextResponse.next();
response.headers.set('X-Custom', 'value');
return response;
}
// Rewrite
return NextResponse.rewrite(new URL('/api/proxy', request.url));