Building a Next.js App With Claude Code
How to use Claude Code to build a full Next.js 16 app — from project setup through App Router, Server Components, and deployment.
Stop configuring. Start building.
SaaS builder templates with AI orchestration.
Problem: Next.js 16 takes real time to set up from scratch. The App Router has breaking changes every major version, async params trip you up on day one, and Claude's training data goes stale faster than the framework ships. You end up correcting Claude more than it helps you.
Quick Win: Start Claude Code from a fresh create-next-app project and let AGENTS.md do the briefing. Claude reads the bundled Next.js 16 docs before touching a single file:
npx create-next-app@latest my-app --typescript --tailwind --app --turbopack
cd my-app
claudeBefore You Start
Three things need to be in place.
Node.js 20.9.0 or newer. Next.js 16 dropped Node 18 support. Check your version:
node --versionClaude Code installed globally:
npm install -g @anthropic-ai/claude-codeA Claude Pro or Team subscription. Claude Code uses your Anthropic account. Free tier won't work.
TypeScript 5.1+. Next.js 16 requires it. Running create-next-app@latest with --typescript installs the right version for you.
Git and a GitHub repo make Vercel deployment one step. Not strictly required, but the deploy section assumes you have both.
Creating the Project
create-next-app@latest scaffolds a Next.js 16 project with Turbopack, TypeScript, and Tailwind CSS v4 wired up. Turbopack is the default bundler now, replacing Webpack. Fast Refresh runs up to 10x faster than the previous setup, and cold builds are 2-5x faster.
Run the scaffold:
npx create-next-app@latest my-app --typescript --tailwind --app --turbopack
cd my-appTwo files in the project root matter for Claude Code: AGENTS.md and CLAUDE.md. The canary release generates both automatically. On the stable release, you may need to create them by hand.
If they're missing, create AGENTS.md in the project root with one line to load the bundled docs:
node_modules/next/dist/docs/Then create CLAUDE.md that imports it. The next section covers what to add beyond that.
Why AGENTS.md matters: it points Claude to bundled documentation at node_modules/next/dist/docs/. Claude reads those docs at session start instead of relying on training data. In Next.js benchmarks, agents with bundled docs hit 100% on Next.js evals. Agents without them miss the breaking API changes introduced in Next.js 16.
CLAUDE.md uses the @AGENTS.md import syntax to pull in those rules without copying them:
@AGENTS.mdClaude reads both files before writing any code.
Customizing CLAUDE.md
The auto-generated CLAUDE.md covers Next.js conventions. Add your project's stack on top:
@AGENTS.md
## Stack
- Next.js 16 with App Router (TypeScript)
- Tailwind CSS v4 with shadcn/ui components
- PostgreSQL via Supabase (use server-side client for Server Components)
- oRPC with Zod for type-safe API routes
## File Conventions
- Server Components by default. Add "use client" only when you need browser APIs or interactivity.
- Route handlers go in app/api/[route]/route.ts
- Database queries run in lib/db.ts, never inline in components
## Commands
- Dev server: npm run dev
- Type check: npx tsc --noEmit
- Build: npm run build
## Proxy
- Middleware logic lives in proxy.ts, not middleware.ts (Next.js 16)The proxy.ts note is worth adding explicitly. Claude still reaches for middleware.ts by default. One line in CLAUDE.md prevents that mistake every time.
Keep CLAUDE.md specific. Vague instructions ("write clean code") don't change Claude's behavior. Concrete file paths, commands, and naming conventions do.
Understanding the App Router With Claude Code
The App Router separates components into two categories. Server Components run on the server and can fetch data directly. Client Components run in the browser and handle interactivity.
Claude reads the App Router rules from AGENTS.md and gets the boundary right without you specifying it. The default is Server Component. You only add "use client" at the top of a file when you need useState, useEffect, browser APIs, or event handlers.
Next.js 16 ships with React 19.2. That includes View Transitions, useEffectEvent, and the <Activity /> component. These are new enough that Claude's training data may not cover them well. For anything using React 19 APIs, add explicit examples to CLAUDE.md or describe what you want in terms of behavior rather than API names.
Ask Claude to build a dashboard page and it places the data fetch in the Server Component and passes results down as props to any interactive parts:
claude "build a /dashboard page that fetches user stats from the database and shows them in a summary card with a refresh button"Claude generates the Server Component at the top of the tree, creates a Client Component for the refresh button, and wires them together. You don't specify which component is which. Claude reads the rules from AGENTS.md and figures it out.
Building a Feature End-to-End
Here's a concrete walkthrough: a user profile page with server-side data and a client-side edit form.
Start in plan mode before any code gets written:
claude --plan "add a /profile page with server-fetched user data and an editable display name form"Claude maps out the files it needs: a page component, a Server Action for the form submission, a database query helper, and a Client Component for the form. You review the plan. Then it builds.
The page file uses async params as required in Next.js 16:
// app/profile/[id]/page.tsx
import { ProfileForm } from "@/components/profile-form";
import { getUser } from "@/lib/db";
interface PageProps {
params: Promise<{ id: string }>;
}
export default async function ProfilePage({ params }: PageProps) {
const { id } = await params;
const user = await getUser(id);
return (
<main className="max-w-lg mx-auto py-12">
<h1 className="text-2xl font-bold mb-6">Your Profile</h1>
<ProfileForm user={user} />
</main>
);
}The params object is a Promise in Next.js 16. await params is required. Claude handles this correctly when AGENTS.md is in place. Without it, Claude writes the old synchronous pattern and the build fails with a type error.
The Server Action for the form lives in a separate file:
// app/profile/actions.ts
"use server";
import { updateUser } from "@/lib/db";
export async function updateDisplayName(userId: string, name: string) {
await updateUser(userId, { displayName: name });
}Next.js 16 Gotchas Claude Handles
A few breaking changes from Next.js 14/15 trip up Claude without AGENTS.md. With it, these work correctly.
| Change | Old pattern | Next.js 16 |
|---|---|---|
| Dynamic params | params.id (sync) | (await params).id |
| Search params | searchParams.query (sync) | (await searchParams).query |
| Cookies/headers | cookies().get(...) | (await cookies()).get(...) |
| Middleware | middleware.ts | proxy.ts |
| Cache config | experimental.dynamicIO | "use cache" directive |
Cache Components and the "use cache" directive. Next.js 16 replaces experimental.dynamicIO with the "use cache" directive. Add it at the top of any component or function you want cached:
// app/stats/page.tsx
"use cache";
import { getStats } from "@/lib/db";
export default async function StatsPage() {
const stats = await getStats();
return <pre>{JSON.stringify(stats, null, 2)}</pre>;
}Claude generates the directive correctly for new files. For code migrated from Next.js 15, tell Claude explicitly: "migrate this file from experimental.dynamicIO to the use cache directive."
Turbopack as default. npm run dev uses Turbopack. Webpack config in next.config.ts is mostly irrelevant unless you have custom loaders. Claude knows this from AGENTS.md and won't generate Webpack-specific config.
The Next.js DevTools MCP
Next.js 16 ships a Model Context Protocol server for AI-assisted debugging. It gives Claude routing context, unified browser and server logs, and automatic access to error stack traces inside the session.
Connect it in your Claude Code settings by pointing to the bundled MCP server:
{
"mcpServers": {
"nextjs-devtools": {
"command": "node",
"args": ["node_modules/next/dist/mcp/index.js"]
}
}
}With the DevTools MCP active, Claude can diagnose a caching issue without you copy-pasting logs. Ask Claude why a route is serving stale data, and it reads the server logs directly, traces the cache key, and tells you which revalidateTag() call to add.
Without this, Claude diagnoses from the code alone. With it, Claude diagnoses from live runtime data. That difference matters most when a bug only shows up in production.
The DevTools MCP also gives Claude access to the routing tree at runtime. If you ask Claude to add a new route and it conflicts with an existing dynamic segment, the MCP surfaces that immediately instead of leaving you to find it at runtime. For complex App Router setups with nested layouts, parallel routes, and intercepting routes, that context is worth having in every session.
Quality Gates Before You Ship
Two commands run before every commit.
Type check with zero errors:
npx tsc --noEmitClean production build with Turbopack:
npm run buildAsk Claude to run both after any significant change:
claude "run tsc --noEmit and fix any type errors, then confirm the build passes"Claude reads the error output, fixes the issues, and re-runs the check. This is where async params mistakes, missing "use cache" directives, and misconfigured Server Actions surface before they hit production.
Zero type errors and a clean build are the minimum bar for shipping. Not a suggestion. The check runs every time.
Deploying to Vercel
Vercel detects Next.js 16 automatically. Two options for deploy.
Git integration: push to your main branch and Vercel builds it. Set environment variables in the Vercel dashboard under Settings > Environment Variables before the first deploy. Vercel reads the build output and infers which routes are static, dynamic, or cached.
CLI deploy: set your environment variables first, then push:
npx vercel env add DATABASE_URL production
npx vercel env add NEXT_PUBLIC_SUPABASE_URL production
npx vercel --prodThe build logs in Next.js 16 show each route's render strategy alongside its output size. Static routes show green, dynamic routes show yellow. This tells you exactly what's pre-rendered at build time and what runs on each request.
If a route you expected to be static shows as dynamic, the DevTools MCP helps diagnose why. Often it's an uncached data fetch that forces the route to run at request time.
What an Orchestrated Pipeline Looks Like
A single Claude Code session handles features well. A coordinated pipeline handles products.
Build This Now runs 32 specialist agents across planning, building, evaluating, and testing before a feature ships. Three planning agents work in parallel before any code is written. After the build, adversarial evaluators review the output and builders fix what they flag. Two test agents run in parallel, one with a real browser. Every feature exits the pipeline with zero type errors, zero lint errors, and a clean Turbopack build.
The npx tsc --noEmit check at the end of this guide is that last gate. In an orchestrated pipeline, it runs automatically after every agent hand-off.
Set up AGENTS.md, write a specific CLAUDE.md, use plan mode before complex features, and run the quality gates before every commit. Those four habits cover most of what makes a Claude Code workflow with Next.js 16 reliable. The framework changes. The process doesn't.
Stop configuring. Start building.
SaaS builder templates with AI orchestration.