How I cut 130 KB from every page by lazy-loading one library
I run a site with 90+ small browser tools (calculators, QR and barcode generators, image tools). Everything ships in one JavaScript bundle, and during a recent performance audit one thing stood out: a QR-code reading lib
I run a site with 90+ small browser tools (calculators, QR and barcode generators, image tools). Everything ships in one JavaScript bundle, and during a recent performance audit one thing stood out: a QR-code reading library (jsQR) was inside the main bundle, so every visitor downloaded it, even people who only opened a salary calculator.
Moving it out of the main bundle and loading it only on the QR tools cut about 130 KB from every other page. Here is the simple pattern, no framework needed.
1. Find out what is actually heavy
Before moving anything, measure. A quick way is to minify the bundle with and without a library and compare sizes, or use a bundle analyzer if you have a build step. In my case the main bundle was over 3 MB of source, and jsQR was one of the biggest single pieces used by only a handful of pages.
Good candidates for lazy loading:
- libraries used by one or two features (QR readers, PDF tools, charting)
- code that only runs after a user action (upload, "scan", "export")
2. Load the script only when needed
A tiny helper that injects a script once and returns a promise:
const loaded = {};
function loadScript(src) {
if (!loaded[src]) {
loaded[src] = new Promise((resolve, reject) => {
const s = document.createElement('script');
s.src = src;
s.async = true;
s.onload = resolve;
s.onerror = () => {
delete loaded[src]; // allow a retry later
reject(new Error('Failed to load ' + src));
};
document.head.appendChild(s);
});
}
return loaded[src];
}
Then call it at the moment the feature is used:
async function scanQrFromImage(imageData) {
await loadScript('/js/vendor/jsqr.min.js'); // exposes window.jsQR
return window.jsQR(imageData.data, imageData.width, imageData.height);
}
If you use ES modules with a bundler, await import('./jsqr.js') does the same job and the bundler splits the file for you.
3. Start loading a little early
Waiting until the click adds a small delay. Two cheap tricks:
- start
loadScript()when the user opens the QR tool page, not when they press "Scan" - or add
<link rel="prefetch" href="/js/vendor/jsqr.min.js">on that page only
4. Bust the cache when you deploy
If you version files with a query string (bundle.min.js?v=12.5), remember to bump it for the lazy file too, otherwise returning visitors may get an old copy.
Result
- Every non-QR page: about 130 KB less JavaScript to download and parse
- QR pages: same behaviour, the library arrives a moment before it is needed
- No framework, no new build tooling
It is not glamorous, but for a site where most visitors land on one tool and leave, shipping only what that page needs is one of the easiest speed wins.
If you want to see the tools this came from, they are at velobranding.com.
Disclosure: I'm the founder of VeloBranding. This post was written with an AI assistant, based on a change we actually shipped.
Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.