.perf/budgets.json
Fields
number
Ceiling on first-load JS, in bytes, for any route without a specific entry.
Record<string, number>
Per-route ceilings, keyed on the URL pattern as printed by
crust analyze
(/products/[slug], not the file path). Overrides the default.number
Maximum tolerated growth versus the baseline, as a fraction.
0.05 is 5%. Requires a baseline;
ignored when there is none.number
Minimum share of a route that must be static, 0–1, for any route without a specific entry. Only
applies to routes that emitted a shell.
Record<string, number>
Per-route shell floors. Overrides the default.
string[]
URL patterns exempt from automatic rendering-mode, cache, and shell-disappearance regressions.
Use this when making a route less static is intentional. Explicit
firstLoadBytes, maxGrowth,
and shell-ratio limits continue to apply.Choosing the axes
All three answer different questions:Bytes alone is the common mistake. A PR that removes a cache directive can halve your static shell
while the bundle stays byte-identical — and a bytes-only check passes it.
Blame
Each breach carries the largest contributor it can name — your module, a dependency, or the call site that postponed a component:Behavior without a comparable baseline
firstLoadBytes and minShellRatio describe the current build and still run without a baseline.
maxGrowth requires a delta and is skipped.
The same rule applies when the bundler, Next.js major, or snapshot schema changed: absolute
ceilings still run, while growth and automatic regression checks do not.
See regressions and certainty for the full comparison model.