Client performance budgets
npx warlock build reports each page’s first-visit JavaScript and CSS after
the Vite client build. Existing apps only receive a report until you configure
limits. New projects start with 150kb JavaScript and 50kb CSS limits.
import { defineConfig } from "@warlock.js/core";
export default defineConfig({ build: { budgets: { page: { js: "150kb", css: "50kb" }, pages: { "/admin/**": { js: "220kb" }, "/admin/reports": { js: "300kb", css: "75kb" }, }, mode: "error", }, },});Sizes accept b, kb, and mb (case insensitive), with 1024 bytes per kb.
Unspecified fields are unlimited; equality passes. page sets defaults.
pages keys match route paths: * stays within a segment, ** spans segments,
and ? matches one non-slash character. Later matching rules override earlier
fields while preserving other defaults. Other glob syntax is rejected.
Configured budgets default to mode: "error", which fails the build and
restores the previous dist. "warn" prints violations and completes normally.
Each violation names the page, JS or CSS budget, actual compressed size and
the largest contributing modules. API-only builds have an empty page report.
Inline payload budget
Section titled “Inline payload budget”build.budgets.payload limits the hydration data a single rendered document
embeds in its HTML: the page, layout and app data, translations, session, the
other hydration slots and the page’s browser route table.
export default defineConfig({ build: { budgets: { payload: { page: "64kb", pages: { "/admin/**": "128kb" }, mode: "warn", }, }, },});page is the limit for every document, and it defaults to 64kb. pages uses
the same route-path globs as the client budgets, and the later matching entry
sets the limit. mode is "warn" (the default) or "error". At runtime
"warn" logs a warning and "error" logs at error level; neither fails a
request. At build, "error" fails warlock build and "warn" prints a warning.
Measured at build
Section titled “Measured at build”A page’s payload has seven sections. warlock build can measure two of them
from what the build knows, for every page, and checks their sum against the
page’s limit:
| Section | At build | How |
|---|---|---|
translations | measured | The page’s pruned dictionary (shared groups excluded) plus the shared-table pointer, for the largest locale. An upper bound: it prunes the whole dictionary, not only the page’s own keys. |
routes | measured | The browser route table of the named API routes the page’s client code reaches. An upper bound: web.namedApiRoutes filters, site scoping and the guest rule only remove routes. |
page | runtime | Loader data exists only in a request. |
layout | runtime | Same. |
app | runtime | Same. |
session | runtime | Resolved per request. |
other | runtime | Locale, route identity and the other hydration slots. |
A build failure lists each page over its limit with the per-section breakdown
(translations, routes) and states which sections were not counted, because
the unmeasured sections only add to the total: a page over its limit at build
is over it at runtime too, up to the upper-bound caveats above. A build that
passes says nothing about page data; the runtime check covers it. The build
does not render pages, so there is no build-time measurement of loader data.
Use page.measurePayload({ limit }) in an end-to-end test to measure a rendered
page’s full payload before release.
Measured at runtime
Section titled “Measured at runtime”The full measurement is a runtime one. A page’s payload depends on its loaders,
which only a request runs, so warlock build --json does not report it.
Documents are measured until their route is reported, once per process. When a
route is over budget, Warlock logs one line for that route with the size of
each section (page, layout, app, translations, routes, session, other) and the
three largest data keys. The check adds one serialization of the payload to
each document. Set page above what a healthy page embeds, or raise it for one
route, rather than turning the warning off.
The end-to-end kit records the same measurement for a loaded document with
page.measurePayload({ limit }) (see Browser end-to-end tests),
which adds it to the run’s evidence under payloads.
What is measured
Section titled “What is measured”The first visit includes hydration, the requested page, its layouts and root,
setup registration modules, and the error boundary. From those roots the report
walks the static imports and CSS references in Vite’s .vite/manifest.json.
Each emitted file is gzipped individually and counted once per page. Shared
chunks count toward every page that needs them. The report excludes HTML,
fonts, images, HTTP headers and cache reuse.
Dynamic imports beyond the required page roots and their static dependencies are reported in separate JS/CSS columns, including their transitive imports. They do not count against first-visit limits. This includes other pages exposed by the same hydration registry, so deferred totals describe reachable assets, not a prediction of what a user will download.
The ten largest modules use Rollup’s chunk.modules[].renderedLength, recorded
by a small build plugin. These are uncompressed rendered-byte estimates for JS
chunk contributors, including zero-length stylesheet wrapper modules. They are
diagnostic rankings rather than compressed per-module costs.
Deltas and machine-readable output
Section titled “Deltas and machine-readable output”The table sorts by combined JS and CSS size, descending, with route names
breaking ties. Deltas compare the same route and source path with the previous
successful build’s build-budgets.json in build.outdir (normally dist).
Keep that output directory between builds to retain the baseline. First builds
and new pages display no delta. Failed builds preserve the earlier baseline.
npx warlock build --json > build-report.jsonStdout contains one JSON object: { success, budgets, error? }; progress is
sent to stderr. budgets contains version: 1, compression: "gzip", pages
and violations. Each page records its route (page), source, byte totals
(js, css), dynamic totals, files, modules and optional delta.
Violations contain page, type, numeric budget and actual, and modules.
A budget failure returns a nonzero exit status while still printing the attempted
report; an earlier build failure may have no budgets value.
The full module attribution is private build metadata in
client/.vite/budget-modules.json. Production asset routes do not expose that
folder. No extra dependency is required.