JavaScript and asset budgets: keeping a fast site fast
Analyze bundles, split and tree-shake code, drop polyfills by targeting Baseline browsers, compress images, subset fonts, tame third-party scripts with facades and async loading, and enforce performance budgets in CI.
Comes up in: Web fundamentals, Frontend system design
Learn#
Priya’s team made their store fast in March. By September it was slow again: no single change was big, but forty small ones added 400 KB of JavaScript.
A performance budget is a limit the team agrees on, such as “170 KB of compressed JavaScript on first load”, checked automatically on every pull request. Performance problems that never ship do not need fixing later:
[
{ "name": "first-load JavaScript", "path": "dist/assets/index-*.js", "limit": "170 kB" },
{ "name": "styles", "path": "dist/assets/*.css", "limit": "30 kB" }
]That is a .size-limit.json file for the size-limit tool, which fails the build when a limit is exceeded.
When a budget fails, a bundle analyzer shows which modules fill the bundle, usually one or two large dependencies.
A third-party script, such as chat widgets, analytics, ads and A/B tests, runs on your main thread with code you do not control, and often costs more than your own app.
State
- budget: script 170 KB
Console
empty
The team sets a budget: 170 KB of compressed JavaScript for the first load of the home page.
Read the steps as text
- The team sets a budget: 170 KB of compressed JavaScript for the first load of the home page.
- A pull request adds a date library with every locale. The build reports 236 KB of JavaScript.
- CI fails the build and links the bundle analyzer report, which shows the locales taking 70 KB.
- The author imports only the needed locale. 168 KB: the check passes and the regression never ships.
Predict#
Predict first
A support chat widget adds 250 KB of JavaScript, but most users never open it. What is the best approach?
A facade looks like the widget but loads nothing until the user shows interest. async still downloads and runs it on every page load, which competes with your own code.
Interview angle#
Say it like this — about 30 seconds
“I set budgets per page type, for compressed JavaScript, images, fonts and third-party code. CI enforces them, for example with size-limit or Lighthouse CI. When one fails, I run a bundle analyzer. Usual fixes are route-level code splitting, tree-shakable imports, replacing heavy libraries, and dropping polyfills by targeting Baseline browsers. For third-party scripts, I load them after the main content, use facades for chat widgets and video embeds, and remove ones nobody uses.”
Follow-up How do you handle the performance cost of third-party scripts?
Follow-up How do you keep a large app's bundle size under control over time?
Follow-up What stops tree shaking from working?
Practice#
Guided
Write the check a CI step runs: totals per asset type, overall and third-party, against budgets.
Done when
- Each set budget is compared with the right total.
- Violations report the actual size, the limit and the overage, in the given order.
- Missing budgets are not checked.
Remember#
Try to answer each card before you open it. Then add them to your reviews.
What is a performance budget?
An agreed limit on size or timing, checked automatically on every change.
What is a facade for a third-party widget?
A lightweight placeholder that loads the real widget only when needed.
What blocks tree shaking?
CommonJS, import-time side effects, and whole-library imports.
How do you drop most polyfills?
Target Baseline browsers in your build, so modern features ship untranspiled.
Go deeper
Four layers. Stop at the one your interview needs; each ends with a quick check.
The core ideaEvery round
Budget JavaScript separately: every kilobyte must be downloaded, parsed and executed on the main thread, which matters most on cheap phones. Images cost bandwidth but not main-thread time.
Check yourself: Why budget compressed JavaScript and not just total bytes?
JavaScript costs CPU to parse and run, not just bandwidth.
How it works underneathSenior rounds
Check yourself: What lets a bundler remove unused exports?
Static ES module imports and code without side effects (tree shaking).
Edge cases and trapsWhere people slip
Check yourself: How does font subsetting help?
It removes glyphs you never use, making font files much smaller.
In productionStaff-level answers
Lighthouse CI can assert resource sizes and metrics on every pull request Source: Lighthouse CI: Configuration. Track field metrics too (QLT.01.06), since lab budgets cannot see real devices.
Check yourself: Name a tool that enforces budgets in CI.
Lighthouse CI (budgets or assertions), or bundle size checks such as size-limit.
Finished the five steps? Mark it, and its cards join your reviews.
Last checked 2026-11-01. Spot a mistake? Use “Report an error” below — fixes are listed on the corrections page.
Interview questions this answers
- How do you handle the performance cost of third-party scripts?
- How do you keep a large app's bundle size under control over time?