In the previous article about code splitting in React Native, we’ve talked about what code splitting and bundling can be used for. It was a high-level introduction to the topic. In this article, which is a technical one, we focus on implementation and approaches including the one with Re.Pack - an Open Source, Webpack-based toolkit to build your React Native application with the full support of the Webpack ecosystem.
What is <cyan>Re.Pack?<cyan>
One of the most important superpowers of Re.Pack is the fact that the toolkit allows developers to implement dynamic content/feature delivery in native and cross-platform apps. Thanks to that, Re.Pack can be a great ally in optimizing the performance stats of the React Native apps, especially when it comes to startup time or the initial download size of the apps.
The toolkit is the successor of Haul, but has a wide range of new features that were not available in Haul including:
- Full Webpack ecosystem of plugins and loaders
- Built-in Flipper support
- Hermes support
- Built-in Hot Module Replacement + React Refresh support
- Asynchronous chunks support
- Fully-featured development server
and many more! The full list of Re.Pack features and documentary can be found on its Github profile.
Learn more: If you haven't heard anything about Re.Pack yet, check out the 7th episode The React Native Show podcast titled "Re.Pack - bringing Webpack to React Native". In this talk Paweł Trysła (aka. Zamotany), the lead engineer at Re.Pack, explains what Re.Pack is, what are the benefits of using this toolkit and discusses the use cases of the toolkit. A must watch!
Implementation of dynamic content in mobile apps - available solutions
Having explained what Re.Pack and code splitting are, let’s explore available options for implementing dynamic content/feature delivery in native and cross-platform mobile applications.
Dynamic content in native applications
Although Android and iOS are not the only native platforms in the world, we will focus on them as they are the most popular native operating systems now.
For Android, one of the ways to implement any dynamic content or feature is to use WebView. While this is the easiest method, it’s also the one with subpar experience - higher resources usage and no native look and feel.
Luckily, there’s a better solution - Play Feature Delivery. These options allow us to create bundles with features using a native language (Java or Kotlin).
On iOS, WebView is our only option. Assuming that we could inject binary code into the running application, the solution would be breaking Apple’s iOS application guidelines, meaning it’s unlikely our application would pass Apple’s App Store review.
Dynamic content in cross-platform apps
Developing a native mobile application with dynamic feature/content delivery means we not only have to write the code twice using different languages but also using vastly different architectures and subpar experience on iOS.
What about dynamic content in cross-platform development? Let’s see how we can implement it with React Native.
React Native with Metro
The next step would be to figure out how to actually split the code using Metro. You might be able to use Metro’s segment API but it’s not widely used and the documentation is almost non-existent.
Keep in mind that if you plan to allow 3rd party developers to provide parts of your application functionality (e.g. 3rd party mini-apps), then you would need to figure out a custom solution to do so and find a way to share at least React and React Native dependencies. Metro isn’t a good candidate for that, as the output is a self-contained executable bundle, while you would need a library-like bundle for sharing purposes.
React Native with Webpack and Re.Pack
With Webpack you are able to introduce code splitting, regardless of whether you want to allow 3rd party code or not. Webpack and its features give developers unique possibilities, including:
- dynamic import() function which allows to create asynchronous chunks
- ability to share dependencies between individual builds
- ability to create library-like bundles and much more
To sum up, the combination of Webpack and Re.Pack allows us to deliver dynamic content delivery with native-like experience across multiple platforms with minimal effort.
<cyan>Code Splitting<cyan> in React Native with Webpack and Re.Pack
Let’s see how code splitting in React Native looks like and works under the hood with Webpack and Re.Pack. But, before we dive deep, let’s establish what a chunk is and what its types are.
There are 2 types of chunks in the context of Re.Pack and React Native:
- local chunk - an additional JS file packed together with the main bundle and shipped to the end user inside an ipa/apk file;
- remote chunk - an additional JS file hosted on the remote server and downloaded by the application on the end user’s device on demand.
Before ChunkManger is ready to be used, it needs to be configured using <rte-code>ChunkManager.configure(…)<rte-code> function:
This code instructs <rte-code>ChunkManager<rte-code> how to resolve remote chunks – what the URL of the chunk is that will be downloaded and executed. There’s no need to specify how to resolve local chunks.
The ChunkManager exposes a function called <rte-code>loadChunk(chunkId: string)<rte-code>. It uses <rte-code>resolveRemoteChunk<rte-code> function from <rte-code>ChunkManager.configure(…)<rte-code> to obtain a URL and passes it to a native site to download the chunk and execute it.
Whether you should call this function yourself or not depends on which approach to code splitting you choose - more on that later.
The <rte-code>ChunkManager<rte-code> should be used as a foundation for implementing code splitting and dynamic content delivery.
How to implement Code splitting in React Native? Approaches
Now, let’s talk about the aforementioned approaches to code splitting and, in general, dynamic content delivery:
Async chunks (dynamic import(…) function)
The easiest approach is to use the dynamic <rte-code>import()<rte-code> function. It produces additional chunks as part of the same compilation (build process).
The file passed to the <rte-code>import()<rte-code> function will become an entry point to the new chunk.
It allows you to quickly introduce code splitting, because most of the heavy lifting is handled by Webpack’s runtime code.
You can use <rte-code>import()<rte-code> function together with <rte-code>React.lazy()<rte-code>:
or to build in-house Mini-apps:
By default, all chunks created by the dynamic <rte-code>import()<rte-code> function are remote chunks. To mark a chunk as local, you provide a name (or RegExp) of a chunk in <rte-code>OutputPlugin<rte-code> in Webpack config:
The most noticeable limitation of this approach is the fact that the chunks are created as part of a single build process. There’s no option to include unknown 3rd party code dynamically - the code has to be available at build time.
Dynamic scripts in Re.Pack
Re.Pack’s <rte-code>ChunkManager<rte-code> allows you to load and execute arbitrary code, so it’s possible to use dynamic script-like chunks - chunks with code that is unknown at build time.
This allows the creation of modular applications, mini-apps/super apps with 3rd-party provided code. There are multiple ways for third-party developers to create such chunks. One which we recommend is to leverage Webpack’s library feature together with externals.
This approach is the most complex one since it relies on a brand new feature of Webpack 5 - Module Federation. You can read more about it here, but in short, it allows us to create mini-apps/super-apps using independent and isolated codebases:
This approach is recommended for the most advanced and enterprise users, due to the high overhead of QA and management involved.
At the time of writing this article, Module Federation is not yet available in Re.Pack. The underlying implementation needed for Module Federation support is similar to async chunks, which means that we are close to bringing it to life. In the meantime, you can start investigating other approaches, as the migration from async chunks/dynamic scripts to Module Federation will be easier in the future compared to the migration from no code splitting to Module Federation.
Future of Re.Pack
For the time being, the code splitting support in Re.Pack is in its early stage. We are focusing on addressing the missing bits needed for a stable and production-ready solution: advanced caching, security-code signing and verification, advanced fetching options, and more.
Once the foundations are laid out and solid, we are going to start working on Module Federation support.