The React Framework for production-grade applications with server-side rendering, static site generation, and full-stack capabilities
npx @tessl/cli install tessl/npm-next@16.0.0Next.js 16.0.0 - React framework for production with App Router (recommended) and Pages Router (legacy).
npm install next@16.0.0 react@19.0.0 react-dom@19.0.0File Structure:
app/
├── layout.tsx # Root layout (wraps all pages)
├── page.tsx # Home page route: /
├── loading.tsx # Loading UI fallback
├── error.tsx # Error boundary
├── not-found.tsx # 404 page
└── blog/
├── page.tsx # Blog index route: /blog
└── [slug]/
└── page.tsx # Dynamic route: /blog/[slug]// app/page.tsx
export default async function Page() {
const data = await fetch('https://api.example.com', {
next: { revalidate: 60 } // Cache for 60s
});
return <div>{await data.json()}</div>;
}// app/counter.tsx
'use client';
import { useState } from 'react';
export default function Counter() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(count + 1)}>{count}</button>;
}App Router vs Pages Router?
Server vs Client Component?
Static vs Dynamic?
'use client';
import { useRouter } from 'next/navigation';
import Link from 'next/link';
const router = useRouter();
router.push('/about');
<Link href="/about">About</Link>import { redirect } from 'next/navigation';
export default async function Page() {
const user = await getUser();
if (!user) redirect('/login');
return <div>{user.name}</div>;
}// ⚡ Time-based revalidation (ISR) - Best for predictable content
const res = await fetch('https://api.example.com', {
next: { revalidate: 3600 } // 1 hour
});
// ⚡ Tag-based revalidation - Best for on-demand updates
const posts = await fetch('https://api.example.com/posts', {
next: { tags: ['posts'] }
});
revalidateTag('posts'); // Later in Server Action
// ⚠️ Force fresh data - Slower, always hits server
const res = await fetch('https://api.example.com', {
cache: 'no-store'
});// app/blog/[slug]/page.tsx
export default async function Post({ params }: { params: Promise<{ slug: string }> }) {
const { slug } = await params;
const post = await getPost(slug);
return <article>{post.title}</article>;
}// app/api/users/route.ts
import { NextRequest, NextResponse } from 'next/server';
export async function GET(request: NextRequest) {
return NextResponse.json({ users: [] });
}
export async function POST(request: NextRequest) {
const body = await request.json();
return NextResponse.json(body, { status: 201 });
}// app/actions.ts
'use server';
export async function createPost(formData: FormData) {
const title = formData.get('title');
await savePost(title);
revalidatePath('/posts');
}
// app/form.tsx
import { createPost } from './actions';
export default function Form() {
return (
<form action={createPost}>
<input name="title" />
<button type="submit">Submit</button>
</form>
);
}// Navigation
import { useRouter, usePathname, useSearchParams } from 'next/navigation';
import Link from 'next/link';
import { redirect, notFound } from 'next/navigation';
// Components
import Image from 'next/image';
import Script from 'next/script';
// Server
import { NextRequest, NextResponse } from 'next/server';
import { revalidatePath, revalidateTag } from 'next/cache';
import { headers, cookies } from 'next/headers';
// Fonts
import { Inter } from 'next/font/google';
import localFont from 'next/font/local';import Image from 'next/image';
<Image
src="/photo.jpg"
alt="Photo"
width={800}
height={600}
priority // For above-the-fold
placeholder="blur"
/>next dev --turbo -p 4000 # Dev server with Turbopack on port 4000
next dev -H 0.0.0.0 # Dev server accessible on network
next build --profile # Production build with React profiling
next build --no-lint # Build without linting
next start -p 8080 # Start production server on port 8080
next lint --fix # Run and auto-fix linting errors
next info # Display system information// next.config.js
const nextConfig = {
reactStrictMode: true,
images: {
domains: ['example.com'],
},
};
module.exports = nextConfig;export const revalidate = 60; // ISR: revalidate every 60s
export const dynamic = 'force-dynamic'; // Always SSR
export const runtime = 'nodejs' | 'edge';export const revalidate = 3600; // Revalidate every hour
export default async function Page() {
const data = await fetch('https://api.example.com');
return <div>{data}</div>;
}import { Suspense } from 'react';
export default function Page() {
return (
<Suspense fallback={<div>Loading...</div>}>
<SlowComponent />
</Suspense>
);
}import { cookies } from 'next/headers';
import { redirect } from 'next/navigation';
export default async function Dashboard() {
const cookieStore = await cookies();
if (!cookieStore.get('token')) redirect('/login');
return <div>Dashboard</div>;
}// app/loading.tsx
export default function Loading() {
return <div>Loading...</div>;
}
// app/error.tsx
'use client';
export default function Error({
error,
reset
}: {
error: Error & { digest?: string };
reset: () => void;
}) {
return (
<div>
<h2>{error.message}</h2>
<button onClick={reset}>Try again</button>
</div>
);
}Server vs Client Components:
Data Fetching:
Async Params (Next.js 15+):
const { id } = params; (params is now a Promise)const { id } = await params; (must await in Next.js 15+)const { q } = searchParams; (searchParams is also a Promise)const { q } = await searchParams; (must await)Image Optimization:
<img> tags (missing optimization)<Image> component and configure images.domainsCaching Issues:
cache: 'no-store' everywhere (loses performance benefits)Route Handlers:
"Error: Headers already sent"
return NextResponse per code path, check for missing return statementsHydration Mismatch
Date.now(), Math.random(), browser-only APIs in Server ComponentssuppressHydrationWarning"Module not found" or Build Errors
.next folder and node_modules, run npm install, restart dev serverrm -rf .next node_modules
npm install
next devImage Optimization Failed
images: {
domains: ['example.com', 'cdn.example.com'],
}Middleware Not Running
middleware.ts is at project root (not in app/), check matcher configexport const config = {
matcher: ['/dashboard/:path*', '/((?!api|_next/static|_next/image|favicon.ico).*)'],
};Fetch Caching Issues
cache: 'no-store' for dynamic data or set revalidation timefetch(url, { cache: 'no-store' }) // Always fresh
fetch(url, { next: { revalidate: 60 } }) // Cache for 60sTypeScript Errors with Params
export default async function Page({
params
}: {
params: Promise<{ id: string }>
}) {
const { id } = await params;
}Recommended Project Structure:
app/
├── (auth)/ # Route group (not in URL)
│ ├── login/
│ │ └── page.tsx # /login
│ └── register/
│ └── page.tsx # /register
├── (dashboard)/ # Route group
│ ├── layout.tsx # Shared dashboard layout
│ ├── page.tsx # /dashboard
│ └── settings/
│ └── page.tsx # /dashboard/settings
├── api/
│ └── users/
│ ├── route.ts # GET/POST /api/users
│ └── [id]/
│ └── route.ts # GET/PATCH/DELETE /api/users/[id]
├── components/ # Shared React components
│ ├── ui/ # UI primitives
│ └── features/ # Feature-specific components
├── actions/ # Server Actions
│ ├── auth.ts
│ └── posts.ts
├── lib/ # Utilities and helpers
│ ├── db.ts
│ └── utils.ts
├── layout.tsx # Root layout
└── page.tsx # Home page
middleware.ts # Root-level middleware
next.config.js # Next.js configurationKey Principles:
(name) to organize without affecting URLsactions/ directory for mutations80% of queries: Start with this file → Quick answers to common patterns
Architectural decisions: See concepts.md for when-to-use guidance
Complete implementations: See patterns.md for production-ready examples
Detailed API reference: See api.md for exhaustive interface definitions
Special files reference: See special-files.md for layout.tsx, page.tsx, etc.
Legacy Pages Router: See pages-router.md - use only for existing apps
Progressive Loading Strategy:
"How do I implement [X]?" → patterns.md
"Should I use [X] or [Y]?" → concepts.md
"What props does [component] accept?" → api.md
"Show me a [pattern] example" → patterns.md
"Why is [X] not working?" → Check "Troubleshooting Quick Fixes" above
"Am I making a mistake with [Y]?" → Check "Common Mistakes to Avoid" above