Bundle size is a budget, not a one-time cleanup. The biggest wins usually come from moving work to the server, removing a heavy dependency and stopping one large component from making the whole page client-side.
Key takeaways
- Target: Keep the first-load JavaScript small enough for the device and network you support.
- Measure field data before choosing a fix.
- Change one bottleneck at a time and re-measure on a mid-range phone.
- A fast lab score is not a substitute for real-user data.
What Bundle size measures
Read the Next.js build output, use the bundle analyzer and check the browser's coverage panel. Compare the first-load JS for each route, not just the total build size.
The fixes that usually move the number
- Prefer server components for data and static UI.
- Import from the specific module, not a package barrel.
- Replace heavy libraries with smaller alternatives or native APIs.
- Dynamic-import editors, charts, maps and rich-text tools.
- Set a CI budget and fail the build when a route grows unexpectedly.
Symptom, cause and fix
| Symptom | Likely cause | What to do |
|---|---|---|
| Every route includes a date library | Global import in a shared layout | Move it to the routes that need it |
| Large icon package | Importing the whole icon set | Import individual icons or use a sprite |
| Page is entirely client-side | use client at the top | Push the boundary down to interactive leaves |
| Bundle grows after each release | No budget or review | Add a bundle-size check to CI |
How to measure it properly
- Read the build output. Record first-load JS per route before changing anything.
- Run the analyzer. Find the largest dependencies and who imports them.
- Check coverage. See how much of the initial JS actually executes.
- Remove or defer one dependency. Measure the route again.
- Add a budget. Set a threshold and review exceptions in code review.
A practical example
// Prefer a direct import over a package barrel
import { format } from "date-fns/format";
// Load a heavy editor only when needed
const Editor = dynamic(() => import("./Editor"), { ssr: false });
Common mistakes
- Optimising the total build size while the first-load route stays heavy.
- Adding a large state or UI library for one component.
- Importing a server-only library into a client component.
- Ignoring the cost of polyfills for older browsers.
A 30-day improvement plan
- Week 1. Collect field data and pick the worst route. Write down the baseline.
- Week 2. Fix the largest bottleneck and deploy behind a normal release.
- Week 3. Test on a mid-range phone and a throttled connection; fix regressions.
- Week 4. Compare field data, document the change and add a monitor or budget.
Field data vs lab data
| Source | What it tells you | How to use it |
|---|---|---|
| Field data | What real users experienced | The source of truth for the metric |
| Lab test | A reproducible diagnosis | Find the cause and test a fix |
| Synthetic monitoring | Availability and regressions over time | Alert on a budget breach |
| RUM segmentation | Device, country and route differences | Target the worst segment |
| Server timing | Database, cache and render time | Separate backend from frontend |
Set a performance budget
| Budget | Example starting point | Why |
|---|---|---|
| LCP | 2.5s or less at p75 | The main content must appear quickly |
| INP | 200ms or less at p75 | Interactions must feel responsive |
| CLS | 0.1 or less at p75 | Layout movement erodes trust |
| JavaScript | A route-level KB budget | Prevents bundle creep |
| Images | Responsive sizes and modern formats | The largest common payload |
| Third-party scripts | A named owner and a cap | Uncontrolled tags are a common regression |
Third-party script policy
- Every script needs an owner, a purpose and a review date.
- Load analytics and marketing tags after consent and after the main content.
- Prefer server-side events where they reduce client JavaScript.
- Test the checkout and sign-up with the full tag set enabled.
- Remove tags that no longer answer a business question.
Frequently asked questions
The target is Keep the first-load JavaScript small enough for the device and network you support. A single fast test is not enough; use the 75th percentile of real page views and segment by mobile and desktop.
No. Start with the routes that matter to revenue or support: checkout, pricing, sign-up, the main product page and the most-read blog template. A fast homepage with a slow checkout is not a fast site.
It depends on traffic. Low-traffic sites can take weeks to accumulate enough data. Use lab tools for immediate feedback, but keep the field metric as the source of truth.
Often yes. Image dimensions, priority, bundle splits, caching and third-party scripts usually deliver more than a visual refresh. Redesign only after the measurement shows the layout itself is the bottleneck.
Sources and further reading
- Next.js package bundling guide (nextjs.org)
- Reduce JavaScript payloads with code splitting (web.dev)
- Web Vitals (web.dev)
Related reading
Need a performance audit?
Send the URL and the metric that matters. I will identify the bottleneck, the likely fix and the cheapest way to prove it worked.
How to cite this guide: Nur Hossain Repon, “Bundle Size Optimization Guide for Next.js and React Apps”, Nhrepon Portfolio, reviewed October 2026, https://nhrepon.com/posts/bundle-size-optimization-guide-for-next-js-and-react-apps.





Comments
No comments yet.