Skip to main content
Place the file at the workspace root. Nothing is enforced until it exists.
.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.

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:
When no source map is available, blame falls back to the unattributed total and says so rather than naming the largest thing it happens to know about.