Everything a developer changes on a Core dna site lives in its theme repository. This guide covers the repository and its build, printing records in templates, stylesheets and client-side code, and the parts of a site around the main content.
L1–L2
Unit 04 · Client-side code
Bundle budgets
Client-side codePage 27 of 35Updated
A budget is a size limit per page type that fails the build when a change exceeds it. Real first-party weights measured on production sites for landing pages, content pages and a checkout, targets for a new build, and the webpack setting that turns a budget from a warning into an error.
Who this is for. The developer or lead who wants a site to stay fast after launch. Afterwards you can set a budget per page type from real numbers, and make the build refuse a change that breaks it, instead of finding out from a slow page months later.
What production sites weigh
First-party JavaScript and CSS loaded by a typical page of each type, measured gzipped (gzip -9) on the committed production files:
Site and page type
JavaScript
CSS
Twig, no jQuery: home page
40 KB
30 KB
New build, no jQuery: any page
under 10 KB, plus shared chunks
21 KB
Split bundles, jQuery as one global: content page
64 KB (33 KB of bundles, 31 KB of jQuery)
28 KB
The same site: checkout
156 KB
40 KB
One bundle each, jQuery bundled twice: any page
136 to 139 KB on two sites, 310 KB on a third
29 to 146 KB
The difference between the rows is not the features: it is whether heavy code is split by page type (Bundle boundaries), loaded on demand (Dynamic imports), and whether jQuery is shipped once or twice (jQuery, loaded once).
Targets for a new build
Page type
JavaScript, gzipped
CSS, gzipped
Landing page
under 60 KB
under 40 KB
Content page
under 100 KB
under 50 KB
Checkout
under 200 KB, provider SDKs loaded on the payment step
under 60 KB
These are this guide's recommendation, set where the leaner production sites already are. Third-party scripts (analytics, chat, the payment provider's own SDK) are not in them: budget those separately, because they arrive from other hosts whatever your build does.
Make the build enforce it
webpack checks sizes itself. With hints: 'error' in the production config, an entry or file over its limit fails the build:
JavaScriptwebpack.config.js (from a production repository)
performance: {
hints: isProd ? 'error' : 'warning',
maxEntrypointSize: 150 * 1024, // all files of one entry, JavaScript and CSS
maxAssetSize: 100 * 1024, // any single file
// on-demand chunks are loaded only when needed: keep them out of the budget
assetFilter: (file) => !/(?:receipt-pdf|stripe-pay|mock-pay)\.bundle\.js$/.test(file)
}
Three details decide whether it works:
webpack measures the files it writes, minified but not compressed, in bytes. Minified JavaScript usually gzips to about a third of its size, so 150 KiB per entry is roughly 45 to 50 KB gzipped.
An entry's size includes its CSS when the stylesheet is part of the entry.
Without hints: 'error' the limit is advice. One production repository sets maxEntrypointSize: 300000 and no hints; its home page entry is about 124 KB of JavaScript and 200 KB of CSS, over the limit on every build, and the warning scrolls past unread.
A gulp build has no such setting. Check the output instead, in the build or in CI, and fail when a file grows past its limit:
Shellbin/check-budgets.sh
#!/usr/bin/env bash
# fail when a built file is over its gzipped budget, in bytes
set -e
check() {
size=$(gzip -9 -c "$1" | wc -c)
if [ "$size" -gt "$2" ]; then echo "$1 is $size bytes gzipped, budget $2"; exit 1; fi
}
check static/js/components.bundle.js 30000
check static/js/checkout.bundle.js 100000
check static/css/styles.css 32000
Set each limit a little above today's size, so the check passes now and fails on the first regression. Raise a limit only on purpose, in a commit that says why.