Beyond the Bundle: Engineering Dynamic Code-Splitting and Component Streaming in Expo
I explore the technical architecture behind breaking down monolithic React Native bundles using Expo's latest dynamic import and RSC capabilities. Learn how to optimize Time-to-Interactive by streaming components directly to the device.

Beyond the Bundle: Engineering Dynamic Code-Splitting and Component Streaming in Expo
For years, we’ve treated React Native bundles like massive shipping containers. We’d pack every feature, every library, and every edge case into a single index.bundle file. As my projects grew, I watched our initial load times—and developer frustration—climb steadily.
But the game has changed. With recent advancements in the Expo ecosystem and Metro's evolution, we finally have the tools to treat mobile architecture with the same granularity we’ve enjoyed on the web. I want to share how I’ve been implementing dynamic code-splitting and component streaming to cut our TTI (Time-to-Interactive) by nearly 40%.
The Monolith Problem
In a standard Expo app, the JavaScript engine (Hermes) has to load the entire bundle into memory before the first screen can even think about rendering. If you have a complex 'Settings' tab with heavy charting libraries, your users are paying for those bytes the moment they open the 'Home' screen.
I realized that if we wanted to scale to a truly enterprise-grade experience, we had to stop delivering the whole world at once.
Step 1: Leveraging Metro’s Async Imports
The first breakthrough for me was realizing that Metro (the bundler for React Native) now supports import() syntax natively when configured correctly. This allows us to create 'async chunks'—separate files that are only fetched when needed.
In my recent architecture, I’ve moved away from standard imports for heavy leaf-nodes. Here’s how I structured our dynamic loader:
By wrapping these in React.lazy, Metro automatically splits these components out of the main bundle. When the user navigates to the Analytics screen, the app fetches that specific chunk over the wire (or from local cache).
Step 2: Route-Based Splitting with Expo Router
Manual lazy loading is great, but the real magic happens at the routing layer. Using expo-router, we can implement route-based splitting. I've found that the best approach is to treat every main tab as a separate entry point.
Expo Router handles the complexity of pre-fetching these routes in the background. My rule of thumb: if a user hasn't touched a feature in their first 30 seconds, it shouldn't be in the initial bundle.
Step 3: The Frontier—React Server Components (RSC) in Expo
The most exciting breakthrough I’ve been experimenting with is Expo’s experimental support for React Server Components. This shifts the paradigm from fetching data to fetching rendered components.
In a traditional flow, we fetch JSON, then the client renders the UI. With RSC in Expo, the server (or an edge function) can stream the component tree directly. This removes the need to ship the rendering logic for that specific component to the client bundle entirely.
By moving the logic for PostCard to the server, I’ve managed to strip out thousands of lines of UI logic from the client-side binary. We are essentially streaming the UI.
Engineering Trade-offs
It’s not all sunshine and rainbows. When you move to a split architecture, you have to solve for:
- Offline Resilience: What happens if a user navigates to a lazy-loaded route while in a tunnel? I’ve had to implement aggressive pre-caching using
expo-file-systemto ensure the 'async' chunks are available locally before they are actually requested. - Navigation State: Managing focus and hardware back buttons during a "loading" state requires a very robust
Suspensestrategy.
The Result
In my last major refactor, our main bundle dropped from 4.2MB to 1.8MB. The perceived performance difference is massive. Users are no longer staring at a splash screen while the engine parses a library for a screen they might never see.
Engineering for mobile is no longer just about writing clean code; it’s about managing the delivery of that code. Beyond the bundle lies a more resilient, faster, and truly modern mobile experience. If you haven't looked at your bundle visualizer lately, I highly suggest you start there. You'll be surprised at how much dead weight you're carrying.