---
url: /docs/features/code-splitting.md
---
# Code splitting
Code Splitting is a technique that splits the code into multiple files, which can be loaded on
demand and in parallel.
It can be used to:
- Optimize the initial size of the application and to improve the startup performance by deferring
the parsing (only with JSC) and execution (JSC and Hermes) of the non-critical code.
- Dynamically deliver content and features to the users based on runtime factors: user's role,
subscription plan, preferences etc.
- **For developers and companies**: split and isolate pieces of the product to improve scalability
and reduce coupling.
Code Splitting is one of the most important features in Re.Pack, and it's based on Webpack's
infrastructure as well as the native module
that allows to execute the additional code on the same JavaScript context (same React Native
instance).
:::info
For dynamic feature delivery, Code Splitting should be used as a mean to optimize the user
experience by deferring the features or deliver existing features only to a subset of users.
:::
Code Splitting with Re.Pack is not designed to add new features dynamically without doing the
regular App Store or Play store release. It can be used to deliver fixes or tweaks to additional
(split) code, similarly to Code Push, but you should not add new features with it.
:::caution
Using Code Splitting to deliver new features without a regular App Store release is likely
going to violate Apple's App Store Terms and your application might be rejected or banned.
:::
:::tip
You should provide access to all the features for the App Store review process.
Also, it might be beneficial to highlight that all split features are closely integrated with
application and cannot work in isolation - you don't want to introduce confusion that your
application might compete with Apple's App Store.
On that note, you might want to avoid using terms like _mini-app_ or _mini-app store_ in favour of
_modules_, _components_, _plugins_ or simply _features_.
:::
## Usage
The specific implementation of Code Splitting in your application can be different and should account for your project's specific needs, requirements and limitations.
In general, we can identify 3 main categories of implementation. All of those approaches are based on the same underlying mechanism: Re.Pack's [`ScriptManager`](/api/runtime/script-manager.md) and the native module for it.
:::tip
Use [Glossary of terms](/docs/resources/glossary.md) to better understand the content of this documentation.
:::
### Generic usage
On a high-level, all functionalities that enable usage of Webpack's Code Splitting, are powered by
Re.Pack's [`ScriptManager`](/api/runtime/script-manager.md), which consists of the JavaScript part and the native part.
The [`ScriptManager`](/api/runtime/script-manager.md) has methods which allows to:
1. Download and execute script - [`loadScript`](/api/runtime/script-manager.md#loadscript)
2. Prefetch script (without executing immediately) - [`prefetchScript`](/api/runtime/script-manager.md#prefetchscript)
3. Resolve script location - [`resolveScript`](/api/runtime/script-manager.md#resolvescript)
4. Invalidate cache - [`invalidateScripts`](/api/runtime/script-manager.md#invalidatescripts)
In order to provide this functionalities, a resolver has to be added using [`ScriptManager.shared.addResolver`](/api/runtime/script-manager.md#addresolver):
```ts
import { ScriptManager, Script } from "@callstack/repack/client";
ScriptManager.shared.addResolver(async (scriptId, caller) => {
// In dev mode, resolve script location to dev server.
if (__DEV__) {
return {
url: Script.getDevServerURL(scriptId),
cache: false,
};
}
return {
url: Script.getRemoteURL(
`http://somewhere-on-the-internet.com/${scriptId}`
),
};
});
```
If the `storage` is provided, the returned `url` from `resolve` will be used for cache management.
You can read more about it in [Caching and Versioning](#caching-and-versioning).
:::info
Do not instantiate `ScriptManager` yourself - use `ScriptManager.shared` to get access to an instance.
:::
Under the hood, the way a script gets loaded can be summarized as follows:
1. `ScriptManager.shared.loadScript(...)` gets called, either:
- Automatically by the dynamic `import(...)` function handled by Webpack, when using [Async chunks approach](#async-chunks)
- Manually when using [Scripts approach](#scripts) or [Module Federation](#module-federation)
2. `ScriptManager.shared.loadScript(...)` is called `scriptId` and `caller` arguments, which are either provided by:
- Webpack, based on it's internal naming logic or a [magic comment: `webpackChunkName`](https://webpack.js.org/migrate/5/#using--webpackchunkname--)
- Manually
3. `ScriptManager.shared.loadScript(...)` resolves the chunk location using `ScriptManager.shared.resolveScript(...)`.
4. The resolved location is compared against previous location of that script, if and only if, `storage` was provided and the script was resolved before.
5. The resolved location is passed to the native module, which downloads if necessary and executes the script.
6. Once the code has been executed the `Promise` returned by `ScriptManager.shared.loadScript(...)` gets resolved.
:::info
[`ScriptManager.shared.prefetchScript(...)`](/api/runtime/script-manager.md#prefetchscript) follows
the same behavior except for #6, where it only downloads the file and doesn't execute it.
:::
### Approaches
There are generally 3 approaches to Code Splitting with Webpack and Re.Pack. Keep in mind that the
actual code you will have to create might be slightly different, depending on your project's
requirements, needs and limitations.
Those approaches should be used as a base for your Code Splitting implementation.
:::tip
It's recommended to read [Generic usage](#generic-usage) first, to understand it on a high-level and
get the necessary context.
:::
#### Async chunks
Async chunks (or asynchronous chunks) are the easiest Code Splitting approach. They are usually
created by using dynamic `import(...)` function, which makes them extremely easy to introduce it
into the codebase.
The async chunks are created alongside the main bundle as part of a single
Webpack compilation, making it a great choice for a modular applications where all the code is
developed in-house.
The usage of async chunks essentially boils down to calling `import(...)` in your code, for example:
```js
const myChunk = await import("./myChunk.js");
```
Async chunks created by dynamic `import(...)` function can be nicely integrated using `React.lazy`
and `React.Suspense`:
```jsx
// MyChunk.js
export default function MyChunk(props) {
return /* ... */;
}
// App.js
const MyChunk = React.lazy(() => import("./MyChunk.js"));
function App() {
return (
Loading...}>
);
}
```
For each file in the dynamic `import(...)` function a new chunk will be created - those chunks will
be remote chunks by default.
:::tip
You can learn more about local and remote chunks in the dedicated [Local vs Remote chunks guide](#local-vs-remote-chunks).
:::
:::tip
To learn more or use async chunks in your project, check out our [dedicated Async chunks guide](#guide-async-chunks).
:::
:::tip
To see `import(...)`, `React.lazy` and `React.Suspense` in action, check out
[Re.Pack's `TesterApp`](https://github.com/callstack/repack/blob/main/apps/tester-app/src/asyncChunks/AsyncContainer.tsx).
:::
:::caution
Don't forget to add resolver using [`ScriptManager.shared.addResolver`](/api/runtime/script-manager.md#addresolver)!
:::
#### Scripts
This approach allows to execute arbitrary code in your React Native application.
It's a similar concept as adding a new `