---
title: "A Step-By-Step Guide to Super App Development With Re.Pack 5"
description: "Learn how to connect a React Native host and mini app with Re.Pack 5 and Module Federation, then load the remote module at runtime."
url: https://www.callstack.com/blog/step-by-step-guide-to-super-app-development
updated: 2026-10-25T20:07:25.019Z
---

# A Step-By-Step Guide to Super App Development With Re.Pack 5

Learn how to connect a React Native host and mini app with Re.Pack 5 and Module Federation, then load the remote module at runtime.

_By Nazar Sydiaha, Software Engineer @ Callstack · Published 2026-07-21 · Category: Super Apps_

A React Native app can grow into many distinct product areas while still feeling like a single, cohesive application to the user. As the app expands, those areas do not necessarily need to be developed and shipped as one large JavaScript bundle. Instead, the native application can provide the shared shell, while individual features or mini apps are built separately and loaded when needed.

[Re.Pack 5](https://re-pack.dev/) makes this architecture possible in React Native through Module Federation. A _mini app_ exposes a module, the _host_ registers it as a remote, and the host imports it at runtime. This guide builds the smallest working version of that setup using two existing React Native applications

Let’s start by defining what you’re going to build.

## What will you build?

The setup contains two roles:

- `HostApp` is the main React Native application installed on the device. It owns the native project, navigation, and application shell.
- `MiniApp` is a separate React Native project that exposes a JavaScript module. It is not installed as a second application on the device.

By the end of the guide, `MiniApp` will expose its root component as a federated module, and `HostApp` will load that component when the user opens a dedicated screen.

We will build the setup in five stages:

1. Verify that both applications work independently.
2. Add Re.Pack to both applications.
3. Expose a module from `MiniApp`.
4. Register the mini app as a remote in `HostApp`.
5. Load the remote module from the host application.

##### For a fuller reference implementation, see our [Super App Showcase](https://github.com/callstack/super-app-showcase), which applies the same host-and-mini-app pattern to a multi-feature fintech application.

![](https://www.callstack.com/assets/6a5f10a7fd289d1a0062a7e7_Super-App-Showcase-examples.webp)

## Prepare the two applications

Before connecting the applications, establish a working baseline and migrate both projects to Re.Pack. This gives each application a known-good starting point before Module Federation introduces a runtime dependency between them.

### Check the prerequisites and project structure

Make sure your environment uses versions supported by Re.Pack 5:

- Node.js 20 or newer
- React Native 0.77 or newer

For real super-app projects, a monorepo is often the most practical starting point. It keeps the host application, mini apps, shared packages, types, and tooling close together. You can start with one of these guides on how to set up a monorepo:

- [React Native Monorepo With Yarn Workspaces](https://www.callstack.com/blog/a-practical-guide-to-react-native-monorepo-with-yarn-workspaces)
- [React Native Monorepo With pnpm Workspaces](https://www.callstack.com/blog/react-native-monorepo-with-pnpm-workspaces)

This guide assumes that you already have two bare React Native applications in a monorepository like this:

```javascript
your-project/
├── apps/
│   ├── HostApp/
│   └── MiniApp/
```

We're going to use the Yarn package manager in this guide, but you can use any package manager you prefer, as long as it supports workspaces.

You can also follow this guide using two independent React Native projects. In that case, run each command from the appropriate application directory.

### Verify both applications

Before changing the bundler, make sure both applications work as standard React Native projects.

Run `HostApp` and confirm that it launches correctly. Then do the same for `MiniApp`.

Because each application will eventually run its own development server, they need to use different ports. In this guide, we will use:

```javascript
HostApp: 8081
MiniApp: 8082
```

Starting from two working applications makes it easier to identify whether a later problem comes from the existing React Native setup or from the Re.Pack configuration.

Once both applications work independently, we can migrate them to Re.Pack.

### Add Re.Pack to both applications

Start with the host application:

```bash
cd apps/HostApp
npx @callstack/repack-init
```

During the setup, select Rspack as the bundler.

The `@callstack/repack-init` command adds the Re.Pack configuration required by an existing React Native application. If the command cannot update your project automatically, refer to [the manual migration guide](https://re-pack.dev/docs/migration-guides/metro).

After the migration, reinstall the project dependencies:

```bash
yarn install
```

For iOS, install the native dependencies again:

```bash
cd ios
pod install
```

Next, repeat the same process for the mini app:

```bash
cd ../MiniApp
npx @callstack/repack-init
```

Select the same Rspack configuration, then reinstall the dependencies and pod:

```javascript
yarn install

cd ios
pod install
```

Both applications are now configured to use Re.Pack.

Before adding Module Federation, run a brief migration checkpoint: start each Re.Pack development server on its assigned port, then launch `HostApp` and confirm that it still runs as it did before the migration. The complete commands appear in the verification section, where you will run both servers together. This checkpoint separates bundler migration problems from federation problems.

At this point, they are still completely independent. The next step is to define the [Module Federation](https://www.callstack.com/services/module-federation-development) contract between them.

We will begin with the mini app because it owns the module that the host will consume.

## Connect the mini app to the host

`MiniApp` is the producer because it exposes the module. `HostApp` is the consumer because it locates and loads that module. Configuring them in that order keeps the contract explicit.

### Expose the mini app

Open:

```javascript
apps/MiniApp/rspack.config.mjs
```

First, update the resolver configuration:

```javascript
resolve: {
  ...Repack.getResolveOptions(env.platform, {
    enablePackageExports: true,
    preferNativePlatform: true,
  }),
},
```

The resolver options control how Rspack finds files and packages:

- `env.platform` contains the platform currently being bundled, such as `ios` or `android`.
- `enablePackageExports: true` enables support for the modern `exports` field in `package.json`.
- `preferNativePlatform: true` tells the resolver to prefer React Native-specific files when they exist, such as `Button.native.tsx`.

The `env` argument is available when the configuration is defined through `Repack.defineRspackConfig`:

```javascript
export default Repack.defineRspackConfig((env) => ({
  // configuration
}));
```

> In this guide, we are going to use **ModuleFederationPluginV2**. If you get stuck at any point, you can refer to [the official Re.Pack documentation](https://re-pack.dev/api/plugins/module-federation-v2#modulefederationv2plugin) for guidance.

First, install the official @module-federation/enhanced package, which is required by Re.Pack.

```shell
cd apps/HostApp
yarn add @module-federation/enhanced

cd ../MiniApp
yarn add @module-federation/enhanced
```

Then, add `ModuleFederationPluginV2` to the `plugins` array:

```javascript
plugins: [
  new Repack.RepackPlugin(),

  new Repack.plugins.ModuleFederationPluginV2({
    name: 'miniApp',
    filename: "miniApp.container.bundle",
    dts: false,
    exposes: {
      './App': './App',
    },
    shared: {
      react: {
        singleton: true,
        eager: true,
      },
      'react-native': {
        singleton: true,
        eager: true,
      },
      'react-native-safe-area-context': {
        singleton: true,
        eager: true,
      },
    },
  }),
],
```

The `name: 'miniApp'` option gives the remote container a stable federation name. The host uses that same name when it registers and imports the remote.

The important part of this configuration is the `exposes` block:

```javascript
exposes: {
  './App': './App',
},
```

It makes the local `App` module available to other Module Federation containers.

The host will later import it using:

```typescript
import('miniApp/App');
```

You are not limited to exposing the root application component. A mini app can expose any module that the host needs, including:

- a screen,
- a component,
- a navigation route,
- a feature entry point,
- or a collection of utilities.

The `shared` block defines dependencies that should be reused by both applications:

- `singleton: true` ensures that only one runtime instance of the dependency is used.
- `eager: true` loads the shared dependency upfront instead of creating a separate asynchronous chunk.

Sharing React and React Native as singletons is particularly important. Running multiple copies of these packages in the same application can lead to runtime errors and inconsistent component behavior.

Finally, add a unique output name:

```javascript
output: {
  uniqueName: 'miniApp',
},
```

The unique name prevents collisions between the runtime modules generated by different federation containers.

`MiniApp` now exposes its `App` component under the federated module name `miniApp/App`.

The next step is to tell `HostApp` where that module can be found.

### **Register the mini app in the host**

Open:

```javascript
apps/HostApp/rspack.config.mjs
```

Add a constant for the mini app development server port near the top of the file:

```javascript
const MINI_APP_PORT = 8082;
```

Keeping the port in a constant makes the remote address easier to find and update.

Update the resolver using the same options as the mini app:

```javascript
resolve: {
  ...Repack.getResolveOptions(env.platform, {
    enablePackageExports: true,
    preferNativePlatform: true,
  }),
},
```

Next, add `ModuleFederationPluginV2` to the host configuration:

```javascript
plugins: [
  new Repack.RepackPlugin(),

  new Repack.plugins.ModuleFederationPluginV2({
    name: 'host',
    dts: false,
    remotes: {
      miniApp: `miniApp@http://localhost:${MINI_APP_PORT}/${env.platform}/mf-manifest.json`,
    },
    shared: {
      react: {
        singleton: true,
        eager: true,
      },
      'react-native': {
        singleton: true,
        eager: true,
      },
      'react-native-safe-area-context': {
        singleton: true,
        eager: true,
      },
    },
  }),
],
```

The `name: 'host'` option gives the host container its own stable federation name.

The `remotes` block tells the host where it can find other federation containers:

```javascript
remotes: {
  miniApp: `miniApp@http://localhost:${MINI_APP_PORT}/${env.platform}/mf-manifest.json`,
},
```

There are two important names in this value:

- The `miniApp` key is the name used in imports such as `miniApp/App`.
- The `miniApp@` prefix must match the `name` configured in the mini app’s federation plugin.

The URL points to the Module Federation manifest generated by the mini app development server.

The platform is included in the path because the server produces platform-specific bundles:

```javascript
http://localhost:8082/ios/mf-manifest.json
```

or

```javascript
http://localhost:8082/android/mf-manifest.json
```

The host and mini app also use the same shared dependency configuration. This allows them to use the React and React Native instances already loaded by the host.

Finally, add a unique output name for the host:

```javascript
output: {
  uniqueName: 'host',
},
```

The infrastructure-level connection is now complete:

- `MiniApp` exposes `./App`.
- `HostApp` registers `MiniApp` as a remote.
- Both applications agree on which dependencies should be shared.

There is one networking detail to keep in mind during development.

The `localhost` address is resolved from the environment where the React Native application is running. Depending on your setup, it may not point to the computer running the mini app server.

If the host cannot fetch the mini app manifest, you may need to:

- use your computer’s local network IP address,
- configure `adb reverse` for Android,
- or change the development server host.

With the bundlers connected, we can now integrate the remote module into the host application’s user interface.

## Load the mini app inside the host

The host needs a simple navigation flow so that the mini app is loaded only after the user opens its screen. This makes the lazy-loading behavior visible and gives us a place to display loading and error states.

Install the required packages in `HostApp`:

```bash
cd apps/HostApp

yarn add \
  @react-navigation/native \
  @react-navigation/native-stack \
  react-native-safe-area-context \
  react-native-screens \
  react-error-boundary
```

For iOS, install the pods again:

```bash
cd ios
pod install
cd ..
```

Rebuild the native application after installing the new dependencies.

For Android:

```bash
yarn android
```

For iOS:

```bash
yarn ios
```

Next, open:

```javascript
apps/HostApp/App.tsx
```

Replace its contents with the following code:

```typescript
import React from 'react';
import {ErrorBoundary} from 'react-error-boundary';
import {NavigationContainer} from '@react-navigation/native';
import {
  createNativeStackNavigator,
  type NativeStackScreenProps,
} from '@react-navigation/native-stack';
import {
  ActivityIndicator,
  Button,
  StatusBar,
  StyleSheet,
  Text,
  useColorScheme,
  View,
} from 'react-native';
import {SafeAreaProvider} from 'react-native-safe-area-context';

type RootStackParamList = {
  Home: undefined;
  MiniApp: undefined;
};

const Stack = createNativeStackNavigator<RootStackParamList>();

const FederatedMiniApp = React.lazy(() => import('miniApp/App'));

function App() {
  return (
    <SafeAreaProvider>
      <NavigationContainer>
        <Stack.Navigator>
          <Stack.Screen
            name="Home"
            component={HomeScreen}
            options={{title: 'Host App'}}
          />

          <Stack.Screen
            name="MiniApp"
            component={MiniAppScreen}
            options={{title: 'Mini App'}}
          />
        </Stack.Navigator>
      </NavigationContainer>
    </SafeAreaProvider>
  );
}

function HomeScreen({
  navigation,
}: NativeStackScreenProps<RootStackParamList, 'Home'>) {
  const isDarkMode = useColorScheme() === 'dark';

  return (
    <>
      <StatusBar
        barStyle={isDarkMode ? 'light-content' : 'dark-content'}
      />

      <View style={styles.container}>
        <Text style={styles.title}>Host App</Text>

        <Button
          title="Go to Mini App"
          onPress={() => navigation.navigate('MiniApp')}
        />
      </View>
    </>
  );
}

function MiniAppScreen() {
  return (
    <ErrorBoundary fallback={<MiniAppErrorFallback />}>
      <React.Suspense fallback={<LoadingScreen />}>
        <FederatedMiniApp />
      </React.Suspense>
    </ErrorBoundary>
  );
}

function LoadingScreen() {
  return (
    <View style={styles.container}>
      <ActivityIndicator size="large" />
      <Text>Loading mini app...</Text>
    </View>
  );
}

function MiniAppErrorFallback() {
  return (
    <View style={styles.container}>
      <Text style={styles.errorTitle}>Mini app unavailable</Text>

      <Text style={styles.errorMessage}>
        Make sure the mini app development server is running and that the
        host can access its federation manifest.
      </Text>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    gap: 16,
    padding: 24,
  },
  title: {
    fontSize: 24,
    fontWeight: '600',
  },
  errorTitle: {
    fontSize: 20,
    fontWeight: '600',
  },
  errorMessage: {
    textAlign: 'center',
  },
});

export default App;
```

The connection between the host and the remote module happens on this line:

```typescript
const FederatedMiniApp = React.lazy(() => import('miniApp/App'));
```

The import contains the two parts configured earlier:

```javascript
miniApp / App
```

- `miniApp` refers to the remote registered in the host configuration.
- `App` refers to the module exposed by the mini app as `./App`.

`React.lazy` delays the import until the component is rendered. In this example, that happens only after the user navigates to the `MiniApp` screen.

The host does not need to include every feature in its initial JavaScript bundle. It can request a mini app when the user needs it.

The runtime sequence looks like this:

1. The user opens the mini app screen.
2. `React.lazy` requests `miniApp/App`.
3. The host reads the mini app federation manifest.
4. The required JavaScript bundle is downloaded.
5. `React.Suspense` displays the loading state while the request is in progress.
6. The mini app component is rendered inside the host application.

The error boundary covers a different part of the process:

```typescript
<ErrorBoundary fallback={<MiniAppErrorFallback />}>
```

It displays fallback content when the remote module cannot be loaded or when it throws an error while rendering.

Loading and error states are important in a super app architecture because remote modules may fail for reasons that do not affect the host itself. The remote server may be unavailable, the user may be offline, or an incompatible remote version may have been deployed.

The host should remain usable even when one mini app is unavailable.

## Run and verify the complete setup

The host and mini app each need their own Re.Pack development server.

Open one terminal and start the host server:

```bash
cd apps/HostApp
yarn react-native start --port 8081
```

Open another terminal and start the mini app server:

```bash
cd apps/MiniApp
yarn react-native start --port 8082
```

Next, run the host application.

For iOS:

```bash
cd apps/HostApp
yarn ios
```

For Android:

```bash
cd apps/HostApp
yarn android
```

Only the host needs to be launched as a native application.

`MiniApp` runs its development server, but it does not need to be installed separately on the device.

When the host opens, press:

```javascript
Go to Mini App
```

The host should display the loading state briefly and then render the component exposed by `MiniApp`.

At this point, the complete flow is working:

![](https://www.callstack.com/assets/6a5f0e9d5015defef7b83b0b_Super-app-process.webp)

Your browser does not support the video tag.

_The iOS host app opens a screen and loads the federated mini app._

Your browser does not support the video tag.

_The Android host app opens a screen and loads the federated mini app._

## Prepare for production

The local setup proves that the host can discover and load the mini app. A production implementation also needs a bundle-delivery strategy, a clear native-code boundary, and a release process that follows store policies.

### Create bundles manually

For release builds, Re.Pack is used automatically through the React Native build process. You can also create a JavaScript bundle for either application manually:

```bash
yarn react-native bundle
```

This runs the React Native CLI `bundle` command using Re.Pack as the configured bundler.

Manual bundling can be useful when you want to confirm that an application produces a static bundle without relying on the development server.

For a production super app, you will also need to decide:

- where remote bundles and manifests are hosted,
- how the host selects a remote version,
- how incompatible versions are rejected,
- how failed downloads are handled,
- and whether previously downloaded bundles should be cached.

The development server used in this guide demonstrates the federation flow, but it is not a production remote-delivery strategy.

### Respect the native-code boundary

Mobile microfrontends are not the same as web microfrontends. On the web, a remote application can download most of the code it needs at runtime. In a mobile application, native code must already be included in the binary installed from the App Store or Google Play.

Module Federation can load JavaScript dynamically, but it cannot add a new native iOS framework, Android library, permission, or native module to an application that has already been installed.

This means that:

- the host must include every native module required by its mini apps,
- React and React Native versions should remain aligned,
- native dependency versions should remain compatible,
- and dynamically delivered modules should be treated as JavaScript-only updates.

For example, imagine that `MiniApp` uses a native camera package. The JavaScript code that calls the camera can be loaded remotely, but the camera package itself must already exist in the native `HostApp` build. If the host was released without that dependency, downloading new JavaScript will not make the native camera implementation available.

This is why teams working on [mobile super apps](https://www.callstack.com/services/super-app-development) need a clear native dependency policy. A mini app should not introduce arbitrary native dependencies without coordinating a new host release.

### Follow App Store and Google Play policies

Dynamic JavaScript delivery must also comply with the policies of the App Store and Google Play. Re.Pack can:

- split features into separate bundles,
- defer loading code that is already part of the application,
- deliver fixes to existing JavaScript behavior,
- and adjust previously released functionality.

It should not be treated as a way to bypass the regular store review process for entirely new application functionality. If a change introduces functionality that users or reviewers would recognize as a new feature, include the required native support and product behavior in a regular store release first. After that release, teams can use Re.Pack to control how the corresponding JavaScript is loaded, divided, or updated.

Your exact release process should always be reviewed against the current requirements of the stores where the application is distributed.

## Summary

In this guide, we created a minimal React Native super-app setup using Re.Pack 5 and Module Federation.

We started with two independent React Native applications and gave each one a clear responsibility:

- `HostApp` owns the installed native application and user-facing navigation.
- `MiniApp` exposes a JavaScript feature module.

We then:

1. added Re.Pack to both applications,
2. exposed `MiniApp/App`,
3. registered the mini app manifest in the host,
4. shared the core React Native dependencies,
5. loaded the remote module with `React.lazy`,
6. and added loading and error states around the remote screen.

This is the smallest useful version of the architecture. A production implementation will usually add more layers, including:

- multiple mini apps,
- shared types and packages,
- version compatibility rules,
- production remote hosting,
- bundle signing or integrity checks,
- caching and rollback behavior,
- monitoring and error reporting,
- production asset handling,
- and ownership boundaries between teams.

Ready to take this two-app setup further? Explore:

- [Super App Showcase](https://github.com/callstack/super-app-showcase): a runnable Re.Pack 5 reference app with federated Trading, Wallet, and Auth mini apps, plus shared runtime dependencies.
- [Mobile Module Federation With Re.Pack: When Runtime Delivery Is Worth the Complexity](https://www.callstack.com/blog/mobile-module-federation-with-re-pack-when-runtime-delivery-is-worth-the-complexity): an article-guidance for deciding when independently delivered modules fit your product architecture.

Good luck!
