Skip to main content

Requirements

  • Next.js 15 or 16, App Router
  • Node 20+
  • A production build. crust refuses to measure next dev — dev output is unminified, unbundled and HMR-laden, so any number taken from it is fiction.
Install @moumen/crust as a development dependency, or use npx directly. The package is scoped; the executable and the product are both named crust.

1. Build, then analyze

The findings come first because a new project has no baseline yet. The complete route table follows so you can inspect every route, including ones that need no action. The snapshot is written to .perf/ using one file per build. Two branches can record builds without editing a shared history file.

Reading the columns

  • First load is the JavaScript downloaded on the first visit, including shared root chunks.
  • Shell is the share of visible rendered output outside pending Suspense boundaries.
  • Mode is static, partial, isr, dynamic, or handler.
  • Indented lines name the reason for a mode or hole.
  • names a component that was postponed out of the shell.

2. See it visually

A single self-contained HTML file: summary figures, the route table, and per-route detail with the modules, dependencies, client boundaries and holes. No server, no integration, nothing external.

3. Compare against a baseline

After a change, rebuild and diff:
crust diff accepts a build id, a git SHA, a branch name, or HEAD~3. History is walked topologically, so a snapshot recorded later on another branch is never mistaken for an ancestor.
The two builds must use the same bundler, Next.js major, and snapshot schema. If they do not, crust names the incompatibility and refuses to assign a cause.

Unlock per-file attribution

Route sizes, rendering modes, dynamic-route blame and shell composition all work out of the box. Tracing bytes to a specific file needs source maps in the production build:
next.config.ts
Most projects do not have this on, so this is usually the difference between “this route is 2.8 MB” and packages/features/src/course/… is 340 kB of it”.
Without maps crust still reports every route total and the full shell analysis — it says the attribution is unavailable rather than guessing. Turn maps on in a dedicated analyze build if you would rather not ship them.

Next steps

Understand regressions

Automatic checks, explicit ceilings, comparability, and intentional exceptions.

Add the CI check

Regression enforcement, budgets, a PR comment, and history on an orphan branch.