Every kilobyte of JavaScript has to be downloaded, parsed and executed before your page becomes interactive. On a mid-range phone that cost is far higher than on a developer laptop, which is why bundle size is one of the most reliable levers for real-world speed.
1. Measure before you cut
Start by looking at what is actually in the bundle. A bundle analyser (for Next.js, @next/bundle-analyzer) shows which packages dominate. You will often find one or two surprises: a whole date library for one format call, or an icon pack imported wholesale.
2. Remove or replace heavy dependencies
- Swap large libraries for smaller ones, or for the platform:
Intl.DateTimeFormatreplaces many date helpers. - Import only what you use. Prefer named imports from packages that support tree shaking, and avoid barrel files that pull in everything.
- Check whether a dependency is needed on the client at all. Work that can happen on the server should.
3. Split code by route and by interaction
Load code when it is needed. Frameworks like Next.js split by route automatically, but heavy components deserve their own chunk: a chart, a rich-text editor, a 3D viewer. Use dynamic import() so the code downloads only when the user opens that part of the interface.
4. Defer what is not critical
Analytics, chat widgets and decorative effects do not need to compete with your content for the first paint. Load them after the page is interactive, or on first interaction.
5. Ship modern JavaScript and compress it
Target modern browsers so you are not shipping unnecessary polyfills, and make sure your host serves Brotli or gzip. Both are one-line changes on most platforms.
6. Keep a budget
Pick a number for your main route, for example 150 KB of compressed JavaScript, and check it in CI. Budgets turn performance from a one-off clean-up into a habit.
In practice, the first three steps deliver most of the gain. We apply this checklist on every Next.js project before launch, and it is one reason our sites hold up on modest devices.