npm vs. Vanilla
Same landing page. Two implementations. What actually gets shipped to the browser.
What was tested
Same landing page, built two different ways, then verified behaviorally identical in a headless browser: dark mode toggle, debounced search, tabs, an accordion, a clipboard-copy toast, and a confirm modal all work the same on both builds, with zero console errors on either page. One version (/no-npm) is three hand-written files and nothing else. The other (/npm) reaches for nine direct dependencies — aos, clipboard, countup.js, dayjs, express, headroom.js, lodash.debounce, micromodal, and toastify-js — plus esbuild to bundle them, which is the default instinct when scaffolding this kind of page today.
What was found
The no-npm build ships index.html (4.4KB), style.css (5.0KB), and script.js (4.7KB) — about 13.9KB total, zero dependencies, viewable instantly by opening the file directly. The npm build installs 81 packages (84 counting duplicated-tree entries) into an 18MB node_modules/ folder, then bundles down to bundle.js (117.7KB) and bundle.css (33.8KB) — roughly 151.5KB shipped to the browser.
That's about 11x more bytes for identical functionality, mostly because AOS, Toastify, Headroom, dayjs, and CountUp each bring their own runtime and default styling along for the ride, none of which the vanilla build needs. Getting to a viewable page differs too: no-npm is instant; npm needs npm install && npm run build — about 5.5 seconds here with a warm cache, longer on a cold cache, in CI, or on a fresh clone.
| Metric | No-npm build (/no-npm) | npm build (/npm) |
|---|---|---|
| Direct dependencies | 0 | 9 |
| Installed packages | 0 | 81 (84 counting duplicated-tree entries) |
node_modules size | — | 18MB |
| Shipped HTML | index.html — 4.4KB | bundled into output below |
| Shipped CSS | style.css — 5.0KB | bundle.css — 33.8KB |
| Shipped JS | script.js — 4.7KB | bundle.js — 117.7KB |
| Total shipped to browser | ~13.9KB | ~151.5KB |
| Time to a viewable build | Instant (open the file) | ~5.5s (warm cache) |
The honest nuance
Not every dependency in the npm build is dead weight for the same reason. dayjs, clipboard, and micromodal genuinely do very little over the one-liners they replace — that gap is closer to the demo's real point than a blanket "frameworks bad" take would suggest. The tabs and accordion, meanwhile, have no realistic dedicated npm package for either build to reach for, so both implementations hand-roll them in plain JS — itself a small data point in the same direction.
express was kept in package.json even though the static demo never imports it; it doesn't end up in the bundled output, but it does inflate the installed package count and node_modules size above — worth knowing if you're comparing those numbers to a "real" npm project.
justhtml