The JavaScript budget.
A JavaScript budget is an upper limit set up front on how much code a page may download and execute — and it may not be exceeded.
JavaScript is a page’s most expensive resource, and its cost does not end with its size. An image is downloaded and decoded; a script is downloaded, parsed, compiled and executed. At the same byte count, a script consumes many times the processor time of an image — and that time is spent on the main thread, the place where the user interface freezes.
This asymmetry explains why the budget is set specifically for JavaScript. A page with two megabytes of images can be slow yet usable; with two megabytes of script it becomes unusable.
How the budget is set
For a budget to work it needs three properties: numeric, measurable and non-negotiable. “Let’s keep the JavaScript low” is not a budget; “the compressed total may not exceed 150 kilobytes” is.
- Pick a unit — compressed size, raw size or execution time? All three can be measured separately.
- Set the limit — the number should derive from the target device and target network, not from the competitors’ average.
- Split the shares — how much for vendor libraries, how much for your own code?
- Bind it to a test — a budget written in a document is not a budget; one living in a test is.
- Block the overrun — the test turning red is not enough; it must stop the deployment.
On this site the budget is: 150 kilobytes gzipped in total, within it roughly 56 kilobytes of vendor libraries and own code under 60. The measured real values — vendor 55.7 kilobytes, own code 28 — sit well under the ceiling, and the verification suite checks on every run.
Keeping the budget below the official target is deliberate. A budget running glued to its ceiling breaks at the first small addition, and at that point either the budget is raised or a rushed optimization happens. Leaving headroom means never being forced to decide in a hurry.
Inventory: what is truly needed
When the budget is exceeded, the first reflex is optimization — code splitting, lazy loading, tree shaking. Yet the much larger gain usually stands one step earlier: is this library truly needed?
On a typical site a notable share of the loaded code exists for a single feature, and that feature is either rarely used or doable with native tools. Date formatting, animation, form validation and icon sets are the classic members of this category.
On this site the vendor list is limited to five modules, and each carries a feature actually in use. The list is not a preference but a constraint: adding new dependencies is explicitly forbidden in the project rules, and external hosts are blocked by the content security policy anyway.
The best code splitting strategy is the library that never loads.
Third-party scripts
The place where the budget breaks most often is code the developer did not write: analytics, chat bubbles, ads, A/B tests, heatmaps. These are usually added by marketing, the technical team may not even know, and on most sites their total climbs to a multiple of the site’s own code.
The problem is not only size but control: a third-party script does not ask you when it will update, and one day it can grow heavy. If your budget depends on that script, your budget is not under your control.
The practical approach has three steps: measure each third-party script’s cost to the main thread separately, ask what you gain in return for that measurement, and defer the rest until after interaction. A chat bubble does not need to exist while the page loads; loading when the user clicks it is enough.
On this site there are no third-party scripts, and the privacy text says no cookies are used; the verification suite measures that on every run. The decision is as much about privacy as performance — and here the two point the same way.
Code splitting and the real gain
After the inventory is done and the needless dependencies removed, code splitting enters for what remains: every page loads only the code it needs. In the classic approach a single large bundle lands on every page, and the user downloads the code of features they will never see.
Splitting has a natural limit: too many pieces increase the request count, and every piece carries its own overhead. The practical balance is splitting by route and separating the genuinely large components on top.
The second technique is deferred loading: a feature loads only when the user needs it. A modal’s content, a chart library or a text editor — none is needed while the page loads.
On this site splitting was not needed because the total is already small: own code is 28 kilobytes after gzip, and the motion layer is used on all pages. At this size, splitting’s complexity produces no gain — and needless complexity is a cost too.
The budget also has a communication side, harder than the technical one. The team requesting a new feature usually does not know how many kilobytes it brings — and even knowing, does not know what the number means. What makes a budget work is translating the number into experience: “this library delays the page half a second on a mid-range phone” persuades far more than “it adds forty kilobytes.”
Making that translation requires measurement: the feature is implemented on a branch, its effect measured in a slowed device simulation, and the decision made with that number. The process looks slow, but once set up it repeats for every feature — and it moves the debate from opinion to measurement.
A limit to close with: a budget sets a ceiling; it does not fix the architecture. A wrong architectural choice — such as a needless client-side framework — keeps the budget under constant pressure, and every new feature becomes an argument. The budget is then showing the symptom; the real decision belongs one layer up.
A practical checklist
Turning this page into an audit step, here is what to check for the JavaScript load. The list is kept short because long lists do not get walked; six items fit in one sitting.
- What is the total gzip size — own code and vendor separately.
- How many dependencies, and each for which feature?
- Any unused libraries — the inventory comes before the optimization.
- What is the third-party total — it usually exceeds the site’s own code.
- Any long tasks — every task over fifty milliseconds freezes the page.
- Does the budget live in a test or in a document?
The values measured on this site: vendor 55.7 kilobytes after gzip, own code 28, third parties zero. A total of 83.7 kilobytes against a ceiling of 150 — the headroom is deliberate.