--- 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 `