All posts
3 min readBy BetterCodePush

How big can a React Native bundle get before it hurts?

There's no hard limit on JS bundle size — which is exactly the problem. Where the real costs kick in (startup, OTA delivery, your bandwidth bill) and how to find out what's actually in yours.

PerformanceBundle SizeOTAReact Native

Developers ask this expecting a number — some ceiling where the bundle stops working. The honest answer is that no such ceiling exists: the store size limits people half-remember apply to the app binary, not to the JavaScript inside it, and neither Hermes nor any OTA mechanism enforces a maximum. Which is worse than a limit, really. A limit would fail loudly. Bundle bloat just quietly charges you in three currencies.

Currency one: startup time

Every byte of your bundle participates in startup. Hermes softens this dramatically — bytecode is compiled ahead of time and memory-mapped rather than parsed — but bigger bundles still mean more I/O and more code initialization, and module-level side effects run regardless of engine. React Native's Hermes documentation explains that its build pipeline compiles JavaScript to bytecode for faster startup, not a bundle-size exemption. Measure startup on representative devices and set a budget from your own baseline; there is no universal MB threshold.

Currency two: OTA delivery

The moment you ship updates over the air, bundle size becomes a per-release, per-device cost. A 15 MB bundle pushed to 100,000 devices is 1.5 TB of transfer for a one-line fix — real money under any bandwidth-metered pricing, as we worked through in what OTA updates actually cost, and real failure surface too: big downloads on flaky mobile networks abandon partway, which shows up as the fleet-adoption lag described in the update-debugging checklist. A provider that supports delta delivery can reduce repeat-download cost, but its behavior and fallback rules are provider-specific. A bloated bundle still costs you on fresh installs and whenever a client has to fetch a full update.

Currency three: the assets you forgot

When a bundle is surprisingly large, the JavaScript is usually not the culprit — the assets riding with it are. A hero image exported at 4x, an animation JSON nobody compressed, three icon fonts of which you use eleven glyphs. Audit JavaScript and assets together; the treemap will tell you where the worthwhile wins are.

Finding out what's actually in there

  • Generate a source-map visualization (tools like source-map-explorer or Expo Atlas work on RN bundles) and stare at the treemap. Everyone finds a surprise the first time — the entire locale data of a date library is the classic.
  • Check your heaviest dependencies for lighter idioms: per-function imports instead of whole-library imports, modern date libraries instead of the legacy giants, and no, you probably don't need that 2 MB utility belt for four functions.
  • Defer rarely used screens with lazy require so their cost is paid on navigation, not at startup.
  • Put a bundle-size check in CI with a soft budget. Not to block merges — to make growth visible. Bloat is never one decision; it's forty invisible ones.

The rule of thumb

Treat bundle size like a budget you review, not a limit you fear. Know your number, know your treemap, watch the trend, and let the occasional cleanup sprint pay for itself in faster startups and a smaller bandwidth bill. And if your updates are megabytes when your changes are lines, fix the delivery mechanism before the diet — shipping only what changed beats shrinking what didn't.

Ship your first update

Create an organization and deploy an over-the-air update in minutes. No billing setup during the beta.

Start with BetterCodePush