vercel/next.js
Development workflow
The framework's iteration loop emphasizes watch builds and skipping isolation for the inner loop, with full builds reserved for branch switches and CI prep.
The inner loop
For changes to packages/next/src/:
# Terminal 1 — watch build (auto-rebuilds on change, ~1-2s per save)
pnpm --filter=next dev
# Terminal 2 — run a single test fast
NEXT_SKIP_ISOLATE=1 NEXT_TEST_MODE=dev pnpm testheadless test/e2e/app-dir/app/index.test.tsNEXT_SKIP_ISOLATE=1 saves ~100s per test run by skipping the per-test packing of Next.js. Do not use it when validating module-resolution changes — the test will use the local dist/ directly and hide bugs that surface only when Next.js is packed as a real npm package. Drop the flag when:
- Adding new
require()paths - Adding new exports from
entry-base.ts - Editing edge route imports
- Validating fixes for previously-broken module resolution
NEXT_TEST_MODE=dev forces dev mode; use NEXT_TEST_MODE=start for production-mode tests.
When to do a full build
pnpm build-all # full bootstrap
pnpm build # JS only (after bootstrap)Use pnpm build-all when:
- Switching branches
- Doing a fresh clone
- Editing Rust code in
crates/orturbopack/ - Before pushing for CI
Use pnpm --filter=next build when:
- Edits are confined to
packages/next/ - You don't need Rust changes to take effect
For type errors only, pnpm --filter=next types is much faster (~10s vs ~60s) than a full build.
Branch hygiene
- Branch off
canary, notmain. Thecanarybranch is the active development branch and is what new releases are cut from. - Use a descriptive branch name. Common patterns:
fix/...,feat/...,refactor/...,chore/.... - Keep PRs focused on one change. The repo regularly rejects "kitchen sink" PRs because they slow review and bisecting.
Commit style
- Concise, descriptive subjects. The squash-merge title becomes the squash-commit subject, so write the PR title like a commit subject.
- No "Generated with Claude Code" or co-author footers.
- Prefer present tense ("Fix X" not "Fixed X").
Splitting tasks
The repo's own contributor guide is explicit about decomposition: split work into small, independently verifiable steps and verify each before moving on. Concretely:
- Reproduce first — write a failing test before fixing.
- Make the smallest passing change — implement, run the failing test, see it pass.
- Add coverage — extend the test for adjacent edge cases.
- Lint and type-check —
pnpm prettier --write <files>+npx eslint --config eslint.config.mjs --fix <files>+pnpm --filter=next types. - Push and open PR as a draft.
For larger refactors, land an enabling PR (no behavior change) before the change-of-behavior PR.
Reading large files
Several files in this repo are >1,000 lines (app-render.tsx, base-server.ts, webpack-config.ts, build/index.ts). The advised pattern is:
- Grep for the symbol or string you care about with line numbers.
- Read targeted line ranges using offset/limit, not the whole file.
- Don't re-read the same range without a code change in between.
For generated files (packages/next/dist/, node_modules/, .next/), search rather than read.
CI cycle and reproducing failures
When CI fails:
node scripts/pr-status.js # auto-detects PR from current branch
node scripts/pr-status.js <number> # specific PRThis script writes analysis files into scripts/pr-status/. The triage rules in priority order:
- Build failures — almost always real
- Lint failures — usually fixable with
pnpm prettier-fixandpnpm lint-fix - Type failures —
pnpm types - Test failures — verify locally before assuming flakiness
For local reproduction of a CI failure, mirror the CI environment variables. The most important ones:
| CI env | Effect |
|---|---|
IS_WEBPACK_TEST=1 |
Forces webpack instead of Turbopack |
NEXT_SKIP_ISOLATE=1 |
Skip packing Next.js (CI uses this for speed; module-resolution bugs may hide) |
__NEXT_CACHE_COMPONENTS=true |
Enables cache components and PPR-by-default |
CI workflow files at .github/workflows/build_and_test.yml show the exact env per job under afterBuild: blocks. Snapshot tests are env-sensitive — when updating snapshots, run with the same flags the failing CI job uses.
Pre-commit hook
The Husky hook at .husky/pre-commit runs lint-staged (configured in lint-staged.config.js) on the staged files. Each hit takes ~2 minutes. To pre-validate before committing:
pnpm prettier --with-node-modules --ignore-path .prettierignore --write <files>
npx eslint --config eslint.config.mjs --fix <files>This runs exactly what the hook will run, so a successful pre-validate guarantees a fast commit.
Working against a real app
The scripts/next-with-deps.sh helper (exposed as pnpm next-with-deps) packs the local Next.js and links it into a target project. Use this when validating an end-to-end scenario that requires a real app shape rather than a test fixture.
For quick-start smoke tests:
pnpm new-test -- --args true smoke-feature e2e
node packages/next/dist/bin/next dev test/e2e/smoke-feature
curl --max-time 10 http://localhost:3000Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.