Introduction

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 Re.Pack?

As we mentioned before, Re.Pack is an Open Source toolkit that allows developers to use Webpack features in building React Native mobile apps (especially in building super apps and mini apps).

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.

Android

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).

iOS

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

Given that React Native uses JavaScript as a language for running out application’s logic, we could instruct React Native to execute new pieces of JavaScript code on demand. However, there’s no such publicly available API in React Native, so you would need to implement a Native Module in order to be able to execute arbitrary JavaScript code.

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

With Re.Pack, you are able to leverage the aforementioned Webpack features inside React Native applications including a Native Module to load additional JavaScript code called ChunkManager.

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.

Code Splitting 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.

Chunk

In Re.Pack a chunk is an additional JavaScript (or Hermes’ bytecode) file. That’s it, we don’t differentiate between how the chunk was created – it will be important later when we’ll talk about approaches to code splitting.

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.

ChunkManager

The central point of code splitting support with Re.Pack is the ChunkManager API. It’s provided by the Re.Pack as a native module and allows you to execute additional JavaScript code.
Before ChunkManger is ready to be used, it needs to be configured using ChunkManager.configure(…) function:

This code instructs ChunkManager 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 loadChunk(chunkId: string). It uses resolveRemoteChunk function from ChunkManager.configure(…) 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 ChunkManager 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 import() function. It produces additional chunks as part of the same compilation (build process).

asyn chunks dynamic import

The file passed to the import() 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 import() function together with React.lazy():

or to build in-house Mini-apps:

By default, all chunks created by the dynamic import() function are remote chunks. To mark a chunk as local, you provide a name (or RegExp) of a chunk in OutputPlugin 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 ChunkManager 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.

Implementing Code Splitting in React Native with Re.Pack

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.

dynamic scripts in repack chart

Module Federation

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:

module federation repack

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.

In summary, the roots aren’t deep but the seeds are planted, so stay tuned and follow Re.Pack on its Github and Twitter profiles!