New in Re.Pack 5.1: Preloading Remotes, Runtime Hooks, and Faster Startups

Re.Pack 5.1 is a significant release that revolves around Over-the-Air (OTA) updates and several other improvements. This release introduces a runtime hooks system for the ScriptManager for better control over script loading, enhances Package Exports/Imports support for improved module resolution, adds support for persistent cache for faster hot startups, and adds full compatibility with React Native 0.79.

Alongside these major features, we’ve included various bug fixes to improve stability and performance. This release is also accompanied by several ecosystem announcements.

Support for Module Federation 2 prefetchRemote in runtime

One of the most requested features in Re.Pack recently was the support for preloading remote modules defined with the ModuleFederationPluginV2. Re.Pack had similar functionality in its own ScriptManager called prefetchScript but it was up to the user to connect the two. To fill this gap, we’ve expanded the Module Federation 2 support with another runtime plugin that utilizes ScriptManager.prefetchScript under the hood.

This functionality is now available out-of-the-box starting from Re.Pack 5.1 and is enabled by default. To use it, you need to have @module-federation/runtime installed, and then in your code, you can use it like this:

import { prefetchRemote } from '@module-federation/runtime';

preloadRemote([
  {
    nameOrAlias: 'MiniApp',
    resourceCategory: 'sync',
  },
]);

For the full API reference, please refer to the official Module Federation 2 documentation.

To get the most out of this feature, you should pair it with ScriptManager.invalidateScripts to ensure that the old assets in cache are removed. Then, you can prefetch the new assets that can be loaded on the next startup:

import { ScriptManager } from '@callstack/repack/client';
import { preloadRemote } from '@module-federation/runtime';
import AsyncStorage from '@react-native-async-storage/async-storage';

// enable caching of scripts in the AsyncStorage
ScriptManager.shared.setStorage(AsyncStorage);

// optionally setup a listener for prefetching events
ScriptManager.shared.on('prefetching', (script) => {
  console.debug('prefetching', script.locator.uniqueId);
});

if (updateIsAvailable()) {
  // invalidate the cache and then preload new assets
  ScriptManager.shared.invalidateScripts().then(() => {
    preloadRemote([
      { nameOrAlias: 'MiniApp', resourceCategory: 'sync', depsRemote: false },
    ]).then(() => {
      console.log('preloaded MiniApp assets');
    });
  });
}

With new assets cached to the device filesystem, all that’s left to do is to load the them on the next startup.

Hooks for ScriptManager

Using Module Federation with Re.Pack 5 is now easier than ever. Most of the confusing parts are now configured automatically under the hood. We want users to use the official Module Federation 2 API as much as possible, and as little Re.Pack ScriptManager API as possible. But sometimes, for mobile-specific functionality, there is no other way than to use the ScriptManager API.

To make it easier to use the ScriptManager API with Module Federation, we’ve introduced a new runtime hook system, which allows you to hook into the script loading process and modify it to your needs. It’s best to show an example of what use cases are best handled by this system.

If you want to add code-signing to your bundles, you would have to add CodeSigningPlugin to the rspack.config, and then enable verification of the bundles through the ScriptManager.addResolver API. With Module Federation 2 in Re.Pack, this was rather cumbersome to do—you would have to drop the runtime ResolutionPlugin that is responsible for the automatic resolution of the remote modules and essentially copy that logic into your own code.

Now, we can tap directly into the resolution process from the outside, and modify the arguments before the loading process begins:

import { ScriptManager } from '@callstack/repack/client';

ScriptManager.shared.hooks.afterResolve(args => {
  args.locator.verifyScriptSignature = 'strict';
  return args;
});

The hooks are a very versatile tool. Some of them, like hooks.afterResolve above, can be called multiple times after each other, and you can even modify the arguments in a waterfall manner (meaning that the output of one hook is the input of the next one). Consider this example of a chain of hooks:

ScriptManager.shared.hooks.afterResolve(args => {
  args.locator.verifyScriptSignature = 'strict';
  return args;
});

ScriptManager.shared.hooks.afterResolve(args => {
  if (args.scriptId === 'authMiniApp') {
    args.locator.retry = 3;
    args.locator.retryDelay = 2500;
  }
  return args;
});

ScriptManager.shared.hooks.afterResolve(args => {
  args.locator.cache = false;
  return args;
});

Hooks are an advanced feature and an escape hatch for cases where achieving the desired behavior with the Module Federation 2 API is difficult. You can learn more about the available hooks and their usage in the ScriptManager API documentation.

Experimental Persistent Cache support

Rspack 1.2.0 introduced a new feature called Persistent Caching. With it, each consecutive startup will use build artifacts from a previous run. It was and still is marked as experimental, but is constantly being iterated upon, and it’s high time we start benefiting from it in Re.Pack. Whether you use Rspack or Webpack, we’ve got you covered — all you have to do is to run the development server like this:

REPACK_EXPERIMENTAL_CACHE=1 npx react-native start 

The flag above will enable persistent caching by default in your setup, irrespective of the bundler used. In the future, this flag will be removed, and persistent caching will be turned on by default in all projects.

With persistent caching enabled, you can expect your hot startups to be more than 6x faster:

Blog Re.Pack 5.1 Comparison
Note: Single platform builds mean that builds were run with npx react-native start --platform ios or npx react-native start --platform android, while both platforms builds are done with just npx react-native start.

Persistent cache comes accompanied by --reset-cache flag, which lets you reset the cache on demand. It behaves just like the equivalent when running start command in Metro from @react-native/community-cli-plugin.

npx react-native start --reset-cache

You can always opt out of persistent cache by settings cache: false in your rspack.config.js or webpack.config.js.

Full support for Package Exports & Imports

In Re.Pack 4, we aligned our module resolution strictly with Metro’s implementation, which became the de-facto standard for React Native. This alignment helped eliminate most module resolution issues. Now, with React Native 0.79’s adoption of Packge Exports by default, we’ve further improved our implementation to ensure full compatibility with this new standard.

While Metro has enabled Package Exports/Imports by default, we’re taking a more gradual approach in Re.Pack. Since we support the current and previous two versions of React Native, we plan to enable Package Exports/Imports by default when React Native 0.81 is released. This gives users time to adapt to the new module resolution system while maintaining backward compatibility.

To use Package Exports/Imports in your setup, you need to enable them through getResolveOptions helper function in your configuration:

import * as Repack from '@callstack/repack';

export default {
  context: __dirname,
  entry: './index.js',
  resolve: {
    ...Repack.getResolveOptions({ enablePackageExports: true }),
  }
};

What's new in Re.Pack ecosystem

While not exactly tied to this release, we are super excited about the community that builds around and supports Re.Pack.

React Native Enterprise Framework

Re.Pack is now supported by the newly released React Native Enterprise Framework. If you are a user of @react-native-community/cli you should definitely give it a try—the CLI startup is faster, provides nice DX, and with remote cache set up, you can skip a lot of native builds and download them from GitHub artifacts storage instead.

Hot Updater

If you are looking for CodePush replacement, there are a few options out there. One that got our attention is Hot Updater by gronxb. It recently added support for Re.Pack. It also allows you to self-host your OTA server. Learn more in their docs.

What’s next for Re.Pack?

The next release of Re.Pack is already in the works! Rspack keeps on improving, and we’re experimenting with enabling parallel-loaders in the next release within Re.Pack custom loaders. This will allow to speed up the JS-based loaders such as babel-loader, which are the biggest bottleneck of bundling React Native apps with Re.Pack. On top of that, we are also planning to add support for TypeScript based configurations. Stay tuned!

Table of contents
Planning a super app with React Native?

We help architect and develop super apps using modular, scalable designs.

Let’s chat
Link copied to clipboard!
//
Insights

Learn more about

Super Apps

Here's everything we published recently on this topic.

Sort
//
Super Apps

We can help you move
it forward!

At Callstack, we work with companies big and small, pushing React Native everyday.

Super App Development

Turn your application into a secure platform with independent cross-platform mini apps, developed both internally and externally.

Module Federation Development

Use Module Federation to provide dynamic features within React Native mobile applications post-release.