diff --git a/docs/docusaurus.config.js b/docs/docusaurus.config.js
index 6070de8..49c0b55 100644
--- a/docs/docusaurus.config.js
+++ b/docs/docusaurus.config.js
@@ -46,6 +46,9 @@ const config = {
'2.x': {
label: '2.x',
},
+ '3.x': {
+ label: '3.x',
+ },
},
},
blog: {
diff --git a/docs/versioned_docs/version-3.x/api/_category_.json b/docs/versioned_docs/version-3.x/api/_category_.json
new file mode 100644
index 0000000..bd503d0
--- /dev/null
+++ b/docs/versioned_docs/version-3.x/api/_category_.json
@@ -0,0 +1,7 @@
+{
+ "label": "API reference",
+ "position": 4,
+ "link": {
+ "type": "generated-index"
+ }
+}
diff --git a/docs/versioned_docs/version-3.x/api/is-cached.md b/docs/versioned_docs/version-3.x/api/is-cached.md
new file mode 100644
index 0000000..ddd19aa
--- /dev/null
+++ b/docs/versioned_docs/version-3.x/api/is-cached.md
@@ -0,0 +1,13 @@
+---
+sidebar_position: 3
+---
+
+# isCached
+
+### `isCached` - check if screen/component was already preloaded
+
+Call this function if you need to check whether the component was already loaded or not:
+
+`isCached(componentName: string)` => `boolean`
+
+- `componentName` - name of the component/screen that was registered in `register` function
diff --git a/docs/versioned_docs/version-3.x/api/preload.md b/docs/versioned_docs/version-3.x/api/preload.md
new file mode 100644
index 0000000..a9e7c80
--- /dev/null
+++ b/docs/versioned_docs/version-3.x/api/preload.md
@@ -0,0 +1,16 @@
+---
+sidebar_position: 2
+---
+
+# preload
+
+### `preload` - as an optimization, you can also decide to preload a component before it gets rendered.
+
+You can select what you'd like to load:
+- `preload().component(componentName: string)` => `Promise`
+
+ - `componentName` - name of your component, that was registered via `register` function
+
+- `preload().group(groupName: string)` => `Promise`
+
+ - `groupName` - name of your group, that was specified in `register` function
diff --git a/docs/versioned_docs/version-3.x/api/register.md b/docs/versioned_docs/version-3.x/api/register.md
new file mode 100644
index 0000000..5763adb
--- /dev/null
+++ b/docs/versioned_docs/version-3.x/api/register.md
@@ -0,0 +1,18 @@
+---
+sidebar_position: 1
+---
+
+# register
+
+### `register` - allow you wrap your component/screen into separate bundle
+
+`register
(params: Object)` - return react component (`P` - component props type, use only in TypeScript projects).
+
+Params:
+- `name` (optional) - you need to specify this param only if you will call `preload` function for this screen
+- `require` - function, that return your presentation component. Example: `() => require('./Presentational.js')` (**DEPRECATED - will be removed in future releases. Use `loader` instead**)
+- `loader` - function, that return your presentation component. Example: `() => import('./View')`
+- `group` (optional) - You need specify it, only if you need to `preload` entire group of screens
+- `static` (optional) - all static members from your presentational component.
+- `cached` (optional) - Default: `true`. Set to `false` if you don't need cache your screens. _**Warning:**_ it may decrease performance of your application.
+- `placeholder` (optional) - React component which will display during screen loading. Should specify if you don't use `preload` for this screen.
diff --git a/docs/versioned_docs/version-3.x/fundamentals/_category_.json b/docs/versioned_docs/version-3.x/fundamentals/_category_.json
new file mode 100644
index 0000000..ea8044f
--- /dev/null
+++ b/docs/versioned_docs/version-3.x/fundamentals/_category_.json
@@ -0,0 +1,7 @@
+{
+ "label": "Fundamentals",
+ "position": 1,
+ "link": {
+ "type": "generated-index"
+ }
+}
diff --git a/docs/versioned_docs/version-3.x/fundamentals/basic-usage.md b/docs/versioned_docs/version-3.x/fundamentals/basic-usage.md
new file mode 100644
index 0000000..83a3921
--- /dev/null
+++ b/docs/versioned_docs/version-3.x/fundamentals/basic-usage.md
@@ -0,0 +1,68 @@
+---
+sidebar_position: 3
+---
+
+# Basic usage
+
+This library contains pretty minimalistic API. And we will look on this a little bit later.
+Let's assume we have next folder structure
+
+ .
+ ├── ...
+ ├── Login # Folder with your screen
+ │ ├── View.jsx # Your presentational component
+ │ ├── styles.ts # Your styles for the component
+ │ └── index.ts # Your entry point to the screen
+ └── ...
+
+And your `index.ts` file looks like this:
+
+```js
+import View from './View';
+
+export default View;
+```
+
+You can easily rewrite it with this library!
+
+```js
+import { register } from 'react-native-bundle-splitter';
+
+export default register({ loader: () => import('./View') });
+```
+
+That's all! You can reload your application to see the results and guarantee this changes will not break anything.
+
+:::tip
+If you are using TypeScript in your project, then you may want to specify `Props` for your lazily loaded component. You can do it in this way:
+
+```ts
+import { register } from 'react-native-bundle-splitter';
+import type {Props} from './types';
+
+export default register({ loader: () => import('./View') });
+```
+:::
+
+:::caution Avoid direct references to lazy-loaded file
+Please, be sure, that you have such `index.ts` file. This library works only in case if you don't have any `import` statements which refers to your file. In other words: be sure that you do **NOT** import this file (`View`) from anywhere in your code.
+:::
+
+To assure, that you are doing everything in correct way this library provides a way for checking the performance.
+
+In order to capture your metrics you should change your `App.js` file:
+
+```diff
+import React, { Fragment, PureComponent } from 'react';
+...
++import { investigate } from 'react-native-bundle-splitter/dist/utils';
+
++console.log('Bundle Info: ', investigate());
+
+class App extends PureComponent {
+...
+}
+```
+
+Usually your output from log will look like `Bundle Info: { loaded: 2731, waiting: 141 }`. Such way allows to understand is your changes make sense or not. After adding new screen to this library count of loaded files should be decreasing. Of course, ideal way is when you load as minimum modules as you can. For example only one page with login. In this case your startup time will be almost the same as just created application. And splash screen will not take a lot of time.
+But it depends absolutely on you and yours architecture solutions. Feel free to play around this library and find suitable way for separating your files.
diff --git a/docs/versioned_docs/version-3.x/fundamentals/enabling-ram-bundle.md b/docs/versioned_docs/version-3.x/fundamentals/enabling-ram-bundle.md
new file mode 100644
index 0000000..c69f1b9
--- /dev/null
+++ b/docs/versioned_docs/version-3.x/fundamentals/enabling-ram-bundle.md
@@ -0,0 +1,89 @@
+---
+sidebar_position: 2
+---
+
+# Enabling Ram Bundle
+
+## RAM... What is it? About RAM Bundle format
+
+### RAM Bundle format
+
+[Metro](https://facebook.github.io/metro/) bundler for react-native offers special format - RAM (Random Access Memory). RAM bundle is a new format of React Native application packaging that helps to optimize load times of your app.
+
+You can read [more](https://facebook.github.io/metro/docs/bundling) about this bundle format.
+
+### Context and motivation
+
+React Native is a still JS application. In the browser all your JavaScript and modules have to be bundled (combined, minified, and maybe even uglified) before deploying. As a result, you drastically reduce the number of files (usually down to one) along with the total size of the code you’re serving by removing all information not necessary for production, i.e., mercilessly rewriting function and variables names, deleting code comments, and so on.
+
+The whole procedure stays pretty much the same when you’re developing a React Native app. It’s still (React-flavored) JavaScript you write there, right?
+
+But here’s a problem: when your app grows, so does the bundle size, and let’s just say that React Native apps are usually more than a few kilobytes in size. The heavier your app bundle is, the more time it needs to be fully loaded into memory, parsed, and executed, before even showing you a splash screen!
+
+However, the difference is that a React Native app is executed in a different environment — on your mobile device running Android or iOS (coming soon on Windows devices) rather than in your browser. This allows React Native to be smarter about what it loads to memory and when. And this is when RAM bundles come into play: they define a new bundling format that enables on-demand loading of individual app modules, resulting in a smoother user experience.
+
+### Formats
+
+The official way to bundle your React Native apps at the moment is using Metro Bundler, which currently supports the following bundling formats:
+
+- **Plain**: Good old, pure JavaScript bundle. This is the default type that doesn’t offer any optimizations.
+
+- **Indexed RAM Bundle**: This format defines a binary file with a header that can be used for quickly finding a specific module. For example, the header can tell us that module number 23 (all JavaScript modules in React Native apps are given numerical identifiers) can be found at offset 320 in the RAM bundle file, and has a length of 430 bytes. How does this help us? React Native runtime now doesn’t need to read the entire bundle file into memory, and can load specific modules only when they are needed (e.g., via inline requires).
+
+- **File RAM Bundle**: With this approach, every module is stored in a separate file with the name `js-modules/${id}.js`, where `${id}` is the module’s ID. This format is used by default on Android devices but has the same purpose: to have fast access to individual modules in your bundle.
+
+## Enabling RAM Bundle feature in your application
+
+For enabling this format in your application you need to do pretty simple steps for each platform.
+
+:::tip Enable per platform
+Although enabling RAM Bundle format is recommended for both platforms, you can only enable it for one if necessary.
+:::
+
+### Android
+
+Open `android/app/build.gradle` and edit your file in the following way.
+
+```diff
+project.ext.react = [
+ entryFile : "index.js",
++ bundleCommand : "ram-bundle",
++ extraPackagerArgs : ["--indexed-ram-bundle"]
+ ...
+```
+
+:::info
+You can choose between formats, since Android support both format (indexed and file). But since iOS support only `indexed` format, would be reasonable to keep both platform in consistent and use `indexed` format. But if you decided for some reasons to use `file` format, you don't need to add `extraPackagerArgs: ["--indexed-ram-bundle"]` line. By default android uses `file` format.
+:::
+
+:::caution Hermes enabled
+If you are trying to enable this feature with Hermes engine, you may faced with application crash. It's a known [issue](https://github.com/facebook/react-native/issues/25730). As a temporary solution you can don't activate this bundle format for Android, since Hermes is using similar [mechanism](https://github.com/facebook/react-native/issues/25730#issuecomment-514115115).
+:::
+
+### iOS
+
+For enabling RAM format for iOS you need to open XCode, select the project, go to `Build Phases`, and edit phase `Bundle React Native code and images`. Before `../node_modules/react-native/scripts/react-native-xcode.sh` you need to add `BUNDLE_COMMAND="ram-bundle"`:
+
+```diff
++export BUNDLE_COMMAND="ram-bundle"
+ export NODE_BINARY=node
+ ../node_modules/react-native/scripts/react-native-xcode.sh
+```
+
+### macOS
+
+Enabling `RAM Bundles` on macOS should be done exactly in the same way as for `iOS`
+
+Just open macOS specific project and repeat the same steps.
+
+### web
+
+No extra steps for `web` as there is no such term as "RAM bundles". Webpack itself will be able to split your JS bundle into small parts and will load them as soon as they are needed.
+
+## Summary
+
+Basically you have completed native set up and now you are ready for using this library!
+
+:::tip
+To be sure, that your application isn't broken after all manipulations you can rebuild your application and see, that it works correctly.
+:::
diff --git a/docs/versioned_docs/version-3.x/fundamentals/getting-started.mdx b/docs/versioned_docs/version-3.x/fundamentals/getting-started.mdx
new file mode 100644
index 0000000..45432fd
--- /dev/null
+++ b/docs/versioned_docs/version-3.x/fundamentals/getting-started.mdx
@@ -0,0 +1,53 @@
+---
+sidebar_position: 1
+---
+
+# Getting started
+
+## What to expect
+
+Splitting your bundle into small pieces allows you reduce size of main bundle, which is loaded on application startup. It means, that you can load only necessary things, such as login pages, which user inevitably will see if open an application. And only "necessary" things will be in main bundle. Thus, this means that **the time of initial launch of the application will be minimized** and **memory consumption will be decreased**, since often in applications is a lot of code (components and screens) that the user may simply not see.
+
+import BundleSplittingImageUrl from "@site/static/img/bundle-splitting.png";
+
+
+
+
+ On green part of this picture, you can see that all components/screens are
+ divided into different groups. It allows you to load the application much
+ faster, because instead of loading the entire bundle (red picture), you can
+ load only the parts that you need.
+
+
+
+## What does this package do?
+
+This package is built on top of **RAM** bundles. In addition to the base functionality provided by React Native, it allows you to:
+
+- avoid boilerplate code (you don't need to write code as per react-native docs - you can simply wrap your component into `register` function);
+- use `preload` API (when you can load screens in advance and the transition to them will be as if the entire bundle was loaded at once);
+- and has web support.
+
+## Features
+
+This library is add-on on react-native API. But this library has some features, and here is list of them:
+
+- **Enhanced cache management**. This library provide a way for caching yours components. This mechanism allows you to improve performance of your application and help to avoid unnecessary reloading yours components, which were already loaded.
+- **Ability to preload component**. You can preload components in background, which user may see in the nearest future. It allows to make UI experience more smooth and use your components from cache without any intermittentions.
+- **Supporting all navigation libraries**. This library is compatible with all most known navigation libraries, such as [react-navigation](https://reactnavigation.org/), [react-native-navigation](https://wix.github.io/react-native-navigation/#/) and more others.
+
+## Installation
+
+Install the react-native-bundle-splitter package in your React Native project.
+
+```bash
+yarn add react-native-bundle-splitter
+# or with npm
+# npm install react-native-bundle-splitter --save
+```
+
+This module does not use any native (platform) dependencies and does not need to be linked. So installation process basically is finished. But you also need to enable `RAM bundles` feature in your application. To see how to do it, please read [the guide](./enabling-ram-bundle).
+
+:::tip Minimal react-native version
+You need to use react-native 0.60 or higher, since feature with inline requires is available out-of-box only from this version.
+:::
diff --git a/docs/versioned_docs/version-3.x/guides/_category_.json b/docs/versioned_docs/version-3.x/guides/_category_.json
new file mode 100644
index 0000000..b07efb9
--- /dev/null
+++ b/docs/versioned_docs/version-3.x/guides/_category_.json
@@ -0,0 +1,7 @@
+{
+ "label": "Guides",
+ "position": 2,
+ "link": {
+ "type": "generated-index"
+ }
+}
diff --git a/docs/versioned_docs/version-3.x/guides/async-loading.md b/docs/versioned_docs/version-3.x/guides/async-loading.md
new file mode 100644
index 0000000..732532f
--- /dev/null
+++ b/docs/versioned_docs/version-3.x/guides/async-loading.md
@@ -0,0 +1,131 @@
+---
+sidebar_position: 3
+---
+
+# Async loading
+
+If you did not load all the screens at the stage of opening the application, then, of course, you need to load them after (for example, if the user logs in and you need to redirect him to another screen, which has not yet been loaded).
+
+This library will load it automatically and cache, if it's needed. However, it can lead to application freezes or the appearance of “white blinks”.
+
+To avoid this, you can load the necessary screens asynchronously.
+
+## One specific screen
+
+More often you need this library when you have any async operations, between navigating. Let's have a look at the next example:
+
+```js
+// home.ts
+import React from 'react';
+import { Button, View, Text } from 'react-native';
+
+const REGISTERED = 'REGISTERED';
+
+const doLoginRequest = (): Promise<{ status: string }> => new Promise((resolve) => {
+ setTimeout(() => resolve({ status: REGISTERED }), 3000);
+})
+
+class HomeScreen extends React.Component {
+ render() {
+ return (
+
+ Login
+
+
+ );
+ }
+
+ doLogin = async () => {
+ const { status } = await doLoginRequest();
+ if (status === REGISTERED) {
+ this.props.navigation.navigate('Dashboard');
+ }
+ }
+}
+```
+
+You can rewrite this code to this
+
+```js
+import { preload } from 'react-native-bundle-splitter';
+
+// ...
+doLogin = async () => {
+ const [{ status }] = await Promise.all([
+ doLoginRequest(),
+ preload().component('Dashboard')
+ ]);
+ if (status === REGISTERED) {
+ this.props.navigation.navigate('Dashboard');
+ }
+}
+// ...
+```
+
+In this case you can be sure, that your component will be loaded before usage and you can make transition to screen in usual way. But in order to do this you need to wrap your `Dashboard` component to `register` function:
+
+```js
+// dashboard/index.ts
+import { register } from 'react-native-bundle-splitter';
+
+export default register({ loader: () => import('./View'), name: 'Dashboard' });
+```
+
+## Group of screens
+
+Of course, applications do not consist of one screen. Therefore, most likely, you will need to load groups of such screens.
+
+Assume you still have `HomeScreen`. But if login is successful, then you need load `Dashboard`, `Settings` and `Profile`.
+
+You can do it in the following way:
+
+```javascript
+// home.ts
+import { preload } from 'react-native-bundle-splitter';
+
+// ...
+doLogin = async () => {
+ const [{ status }] = await Promise.all([
+ doLoginRequest(),
+ preload().group('LOGGED')
+ ]);
+ if (status === REGISTERED) {
+ this.props.navigation.navigate('Dashboard');
+ }
+}
+// ...
+```
+
+And for screens:
+
+```js
+// dashboard/index.ts
+import { register } from 'react-native-bundle-splitter';
+
+export default register({ loader: () => import('./View'), group: 'LOGGED' });
+```
+
+```js
+// settings/index.ts
+import { register } from 'react-native-bundle-splitter';
+
+export default register({ loader: () => import('./View'), group: 'LOGGED' });
+```
+
+```js
+// profile/index.ts
+import { register } from 'react-native-bundle-splitter';
+
+export default register({ loader: () => import('./View'), group: 'LOGGED' });
+```
+
+So, what are you doing here? Instead of specifying `name` and calling `preload().component()` you specify `group` for screens, and call `preload().group()`.
+
+## Summary
+
+This library does not do any magic. It simply allows you to transfer the time (which is required to load the bundle at the start of the application) to runtime execution.
+
+This way you can load additional parts of the application when doing other asynchronous operations (such as HTTP calls, reading data from storage, etc.) and reduce initial load times.
diff --git a/docs/versioned_docs/version-3.x/guides/metro.md b/docs/versioned_docs/version-3.x/guides/metro.md
new file mode 100644
index 0000000..d260284
--- /dev/null
+++ b/docs/versioned_docs/version-3.x/guides/metro.md
@@ -0,0 +1,79 @@
+---
+sidebar_position: 4
+---
+
+# Metro configuration updating
+
+On this stage we are gathering info about which modules will be included in "startup" bundle. And we are sending this information to metro-bundler, so this tool is aware about that.
+
+## Initial Configuration
+
+In order to see which files should be loaded on initial start of app you can use helper utility (use this code in `App.js`):
+
+```diff
++import { investigate } from 'react-native-bundle-splitter/dist/utils';
+
++console.log(`module.exports = ${JSON.stringify(investigate().loaded.sort())};`);
+```
+
+Then grab this text and put it in a file named `packager/modules.ios.js` (if you are running your app on iOS platform) or to `packager/modules.android.js` (if you run Android app respectively).
+
+## Metro Configuration
+
+Then edit your `metro.config.js` as shown below:
+
+```js
+const resolve = require('path').resolve;
+const fs = require('fs');
+
+// Update the following line if the root folder of your app is somewhere else.
+const ROOT_FOLDER = resolve(__dirname, '.');
+
+const config = {
+ transformer: {
+ getTransformOptions: (_, { platform }) => {
+ let modulePaths = [];
+ const moduleMap = {};
+
+ if (platform === 'android') {
+ modulePaths = require('./packager/modules.android');
+ } else {
+ modulePaths = require('./packager/modules.ios');
+ }
+
+ modulePaths.forEach(path => {
+ if (fs.existsSync(path)) {
+ moduleMap[resolve(path)] = true;
+ }
+ });
+
+ return {
+ preloadedModules: moduleMap,
+ transform: {
+ inlineRequires: {
+ // `blacklist` for RN < 0.64
+ blockList: moduleMap,
+ },
+ },
+ };
+ },
+ },
+ projectRoot: ROOT_FOLDER,
+};
+
+module.exports = config;
+```
+
+Using info from `packager` folder `metro-bundler` will be aware of the fact, which modules should be included in "startup" bundle, and which modules can de dynamically loaded during runtime of application.
+
+:::tip Try to copy output several times
+Once you've added content in `packager` folder and changed `metro` configuration - I highly recommend you to restart `metro` and copy/paster output from `investigate` function into `packager` content once again.
+
+Most likely you will get different output and it's quite reasonable, since you started using ram bundles and now your app starts to load modules only which are really needed for the startup.
+
+I recommend to repeat this operation several times (restart `metro`, copying/pasting new content into `packager` folder), until the output will be constant.
+:::
+
+:::info New cycle dependencies
+It's fine if you start receiving warnings, that cycle dependencies were detected. Since you are loading modules lazily - bundler can not compute all dependencies tree in advance and build it in optimized way, so you will have to resolve it yourself manually and break cycle dependencies.
+:::
diff --git a/docs/versioned_docs/version-3.x/guides/navigation.md b/docs/versioned_docs/version-3.x/guides/navigation.md
new file mode 100644
index 0000000..7b414b3
--- /dev/null
+++ b/docs/versioned_docs/version-3.x/guides/navigation.md
@@ -0,0 +1,161 @@
+---
+sidebar_position: 1
+---
+
+# Navigation libraries integration
+
+As was mentioned in the description of this library - you can easily split your bundle by navigation routes. And this library may be easily integrated with almost all navigation solutions. in this chapter we will have a look on the most popular solutions for navigation: [react-navigation](https://reactnavigation.org/) and [react-native-navigation](https://wix.github.io/react-native-navigation/#/)
+
+Let's consider basic example with two screens. Let's assume we have the next application structure:
+
+## Basic architecture
+
+```jsx
+// home.ts
+import React from 'react';
+import { Button, View, Text } from 'react-native';
+
+class HomeScreen extends React.Component {
+ render() {
+ return (
+
+ Home Screen
+
+
+ );
+ }
+
+ private goToDetails = () => {
+ // call specific function to perform navigation
+ // in case of `react-navigation`: this.props.navigation.navigate('Details')
+ // see routes definition below for each library
+ }
+}
+```
+
+```jsx
+// details.ts
+import React from 'react';
+import { Button, View, Text } from 'react-native';
+
+class DetailsScreen extends React.Component {
+ render() {
+ return (
+
+ Details Screen
+
+
+ );
+ }
+
+ private goToHome = () => {
+ // call specific function to perform navigation
+ // in case of `react-navigation`: this.props.navigation.navigate('Home')
+ // see routes definition below for each library
+ }
+}
+```
+
+## React Navigation integration
+
+Your App container will be the same and there is no any changes.
+
+```jsx
+// App.ts
+import React from 'react';
+import { createStackNavigator, createAppContainer } from 'react-navigation';
+
+import { AppNavigator } from './navigator';
+
+const AppContainer = createAppContainer(AppNavigator);
+
+export default class App extends React.Component {
+ render() {
+ return ;
+ }
+}
+```
+
+All changes will be only in route declaration.
+
+### Before
+
+```js
+// navigator.ts
+import { createStackNavigator } from 'react-navigation';
+
+import DetailsScreen from './details';
+import HomeScreen from './home';
+
+export const AppNavigator = createStackNavigator(
+ {
+ 'Home': HomeScreen,
+ 'Details': DetailsScreen
+ },
+ {
+ initialRouteName: 'Home'
+ }
+);
+```
+
+### After
+
+```js
+// navigator.ts
+import { createStackNavigator } from 'react-navigation';
+import { register } from 'react-native-bundle-splitter';
+
+export const AppNavigator = createStackNavigator(
+ {
+ 'Home': register({ loader: () => import('./home') }),
+ 'Details': register({ loader: () => import('./details') })
+ },
+ {
+ initialRouteName: 'Home'
+ }
+);
+
+```
+
+## React Native Navigation Integration
+
+Similarly to `react-navigation` you need only change declaration of your routes - just wrap all screens, that you want to postpone for loading in `register` function.
+
+### Before
+
+```typescript
+import { Navigation } from 'react-native-navigation';
+
+import DetailsScreen from './details';
+import HomeScreen from './home';
+
+Navigation.registerComponent('Home', () => HomeScreen);
+Navigation.registerComponent('Details', () => DetailsScreen);
+```
+
+### After
+
+```typescript
+import { Navigation } from 'react-native-navigation';
+import { register } from 'react-native-bundle-splitter';
+
+const DetailsScreen = register({ loader: () => import('./details') });
+const HomeScreen = register({ loader: () => import('./home') });
+
+Navigation.registerComponent('Home', () => HomeScreen);
+Navigation.registerComponent('Details', () => DetailsScreen);
+```
+
+## Summary
+
+As you saw in both cases (integration with `react-navigation` and `react-native-navigation`) you just need to wrap your screens, that you want to postpone for loading, in `register` function.
+
+:::tip
+As you can see in this section we used `register` function directly in the navigation declaration, unlike [Basic usage](../fundamentals/basic-usage.md), where we used `register` in the `index` file. You can choose which way is more preferable for you: use this HOC in the routes declaration or in `index` files. No need to use it twice.
+:::
diff --git a/docs/versioned_docs/version-3.x/guides/static-options.md b/docs/versioned_docs/version-3.x/guides/static-options.md
new file mode 100644
index 0000000..b56e037
--- /dev/null
+++ b/docs/versioned_docs/version-3.x/guides/static-options.md
@@ -0,0 +1,62 @@
+---
+sidebar_position: 2
+---
+
+# Static options
+
+Sometimes you need to specify `static` members for your Presentation class.
+
+```javascript
+class HomeScreen extends React.PureComponent {
+ static navigationOptions = {
+ title: 'Home',
+ };
+
+ /* render function, etc */
+}
+```
+
+Unfortunately you can't just wrap your screen with `register` function if you are using any `static` class members in your function.
+
+In order to make it working you need to pass all your `static` to register component you can do it in the following way:
+
+```ts
+import { register } from 'react-native-bundle-splitter';
+
+export default register({
+ loader: () => import('./View'),
+ static: {
+ navigationOptions: {
+ title: 'Home'
+ }
+ }
+});
+```
+
+Of course, you can pass not only plain objects, but functions as well:
+
+```ts
+import { register } from 'react-native-bundle-splitter';
+
+export default register({
+ loader: () => import('./View'),
+ static: {
+ navigationOptions: ({ navigation }) => {
+ // all code from your component
+ return {
+ title: 'Home'
+ }
+ }
+ }
+});
+```
+
+And `navigationOptions` already will not be used from your presentational class, so you can remove it.
+
+```ts
+class HomeScreen extends React.Component {
+ // navigationOptions are removed - now they are in register function
+
+ /* render function, etc */
+}
+```
diff --git a/docs/versioned_docs/version-3.x/guides/upgrading-from-1.x.md b/docs/versioned_docs/version-3.x/guides/upgrading-from-1.x.md
new file mode 100644
index 0000000..969e3bd
--- /dev/null
+++ b/docs/versioned_docs/version-3.x/guides/upgrading-from-1.x.md
@@ -0,0 +1,35 @@
+---
+sidebar_position: 5
+---
+
+# Upgrading from 1.x
+
+## Breaking changes
+
+Fortunately, this release has fully backward compatibility with `1.x.x` release. But I'd like to highlight some important changes:
+
+- `require` property in `register` function now marked as deprecated and will be removed in future releases.
+
+:::tip
+Use new syntax and `loader` property instead.
+:::
+
+- `investigate` returns only initially loaded modules.
+
+:::info
+This behavior was originally planned, but as it turned out, if you call this function at different times of execution of your application, it will show different results, since some modules will be loaded. Thus, the results are not deterministic and can be confusing. Therefore, now this function returns only those modules that are loaded when the application starts.
+:::
+
+- screen gets mounted in `async` way.
+
+:::info
+Earlier, if navigation occurred to a screen that had not yet been loaded, the application would freeze, and the transition occurred only when the screen was actually loaded. This was undesirable behavior, so now, the transition will occur immediately, but if the screen is not cached yet, you will see a blank screen and as soon as it loads, you will actually see it. To avoid this use the `preload` API or `placeholder` option in `register` function.);
+:::
+
+## Migration
+
+If you used the API of the library correctly before, then there will be no changes for you and you can migrate to the second version.
+
+However, after updating to newest version you will get warnings, about the fact, that you should use **`loader` instead of `require`**.
+
+If you want ot get rid off of that, you can do a quick migration. Just replace next expression `require: () => require(` to `loader: () => import(` expression. All modern IDEs allow you to do it for all project. That's all. Congratulations 🎉
diff --git a/docs/versioned_docs/version-3.x/guides/upgrading-from-2.x.md b/docs/versioned_docs/version-3.x/guides/upgrading-from-2.x.md
new file mode 100644
index 0000000..f79ba04
--- /dev/null
+++ b/docs/versioned_docs/version-3.x/guides/upgrading-from-2.x.md
@@ -0,0 +1,20 @@
+---
+sidebar_position: 6
+---
+
+# Upgrading from 2.x
+
+## Breaking changes
+
+Fortunately, this release is fully backward compatible with the `2.x.x` release. The only thing is that minimal supported version of React Native was raised to `0.60.0`.
+
+## Key changes
+
+- the library now uses `Suspense` component internally, so in future it'll be easier to adopt concurrent react features.
+- internally the cache layer was migrated from `Object`s to `Map`s, which in turn should improve the performance a little bit.
+
+## Migration
+
+I believe that for all projects that are using this library, there will be no changes and you can simply bump the version of the library in `package.json` to `3.x.x`.
+
+If you use `react-native` version `0.59.x` then you should stick to `2.x.x` version of the library.
diff --git a/docs/versioned_docs/version-3.x/recipes/_category_.json b/docs/versioned_docs/version-3.x/recipes/_category_.json
new file mode 100644
index 0000000..cd05fba
--- /dev/null
+++ b/docs/versioned_docs/version-3.x/recipes/_category_.json
@@ -0,0 +1,7 @@
+{
+ "label": "Recipes",
+ "position": 3,
+ "link": {
+ "type": "generated-index"
+ }
+}
diff --git a/docs/versioned_docs/version-3.x/recipes/bundle-analysis.md b/docs/versioned_docs/version-3.x/recipes/bundle-analysis.md
new file mode 100644
index 0000000..0137dc0
--- /dev/null
+++ b/docs/versioned_docs/version-3.x/recipes/bundle-analysis.md
@@ -0,0 +1,81 @@
+---
+sidebar_position: 1
+---
+
+# Bundle analysis
+
+More often you need to understand how much space you can save using this library. The simplest way for performing this - compare files size. But it's a pretty routine task. So you can save a little bit time and get great bundle analytics using such tools as [visualize-bundle](https://github.com/JonnyBurger/npx-visualize-bundle) and [react-native-bundle-visualizer](https://github.com/IjzerenHein/react-native-bundle-visualizer)
+
+## Bundle analysis
+
+### Visualization of the bundle
+
+Since `react-native-bundle-visualizer` has some issues with typescript I recommend you use `visualize-bundle` library.
+First of all you need to install it:
+
+```bash
+yarn add visualize-bundle --dev
+# or with npm
+# npm install visualize-bundle --save-dev
+```
+
+After installation edit your `package.json`:
+
+```diff
+{
+ "scripts": {
++ "bundle:analysis": "visualize-bundle"
+ }
+}
+```
+
+And then run analyzer:
+
+- In one terminal:
+
+```bash
+react-native start
+```
+
+- In the second terminal
+
+```bash
+yarn run bundle:analysis
+# or with npm
+# npm run bundle:analysis
+```
+
+You will see something like this:
+
+
+
+It looks terrible, but let's see a little bit deeply into this picture.
+
+### Analysis of the bundle
+
+First of all you need to look at big rectangles, since they are the most significant. In this picture you can see, that in `node_modules` you have several libraries, with pretty big size, such as `moment`, `lodash`, `react-native-firebase` and others. On other hand we have pretty big `assets` folder and `src` (screens and components).
+
+And here is few tactics for proper splitting the final bundle:
+
+- As an alternative for `moment` you can select any other library that will fit for your goals and require less memory
+- For `lodash` you can use not absolute import and import each function as a separate (instead of `import { isEqual } from 'lodash'` you can write `import isEqual from 'lodash/isEqual'`)
+- Firebase from `6.0.0` version is not monorepo and for every feature (notification/crashlytics etc) you can use separate package
+- All yours code and assets you can wrap in HOC provided by `react-native-bundle-splitter` and it should reduce the size of initial loaded bundle
+
+It's common tactics that you may apply in order to simplify your entry point of an application.
+
+## Analysis of the content in `packager` folder
+
+In this folder you should have `modules.android.js` and `modules.ios.js` files. If you open any of them and will try to search for `/node_modules` you may find a lot of matches.
+
+
+
+In the example above we see, that `react-native` uses different versions of `@babel/runtime` and `fbjs`. It often happens, when you have a large project with a lot of 3rd party dependencies. And all of these libraries use different versions of common libraries.
+
+Because of this, tree dependency becomes complicated and bundle grows because of "duplication" of dependencies. But it can be easily fixed, if you dig into `package-lock.json`/`yarn.lock` file and manually re-organize dependency tree. For `package-lock.json` you may follow [this](https://stackoverflow.com/a/48524488/9272042) instruction. And for `yarn.lock` you may use [resolutions](https://classic.yarnpkg.com/en/docs/selective-version-resolutions/) approach.
+
+Thus, by explicitly specifying common dependencies for some packages, you can avoid "duplication" (when the same package is used many times but with different versions) and make your bundle smaller.
+
+## Summary
+
+The main goal of this article is to demonstrate a way for determining the order and prioritizing thing where you should work. Via visualization of bundle you can understand, what exactly may slow down your startup of an application and understand possible way for resolving it.
diff --git a/docs/versioned_docs/version-3.x/recipes/capturing-startup-time.md b/docs/versioned_docs/version-3.x/recipes/capturing-startup-time.md
new file mode 100644
index 0000000..4df77ef
--- /dev/null
+++ b/docs/versioned_docs/version-3.x/recipes/capturing-startup-time.md
@@ -0,0 +1,90 @@
+---
+sidebar_position: 2
+---
+
+# Capturing startup time
+
+The main purpose of this package is splitting bundle by two parts: the first, relatively small which you can load at the start of your application, and the second, where you will keep the rest part of your application. Such way allows significantly decrease initial **startup time** and practically reduce it to the same time as in an empty application created via `react-native init` command. Let's look how we can capture the startup time of an application and understand, how much did you win with the usage of this library.
+
+Below are two ways to measure the start time of the application.
+
+## Using `react-native-startup-time` library
+
+### Library that you need to install
+
+First of all you need to install 3rd party package, that allows you to capture the startup time. I'd recommend you to start using [react-native-startup-time](https://www.npmjs.com/package/react-native-startup-time). So let's start with this library.
+
+```bash
+yarn add react-native-startup-time
+# or with npm
+# npm install react-native-startup-time --save
+```
+
+:::info
+This library is need in react-native linking. So if you are using react-native <= 0.60 you need to go through manual step of linking that you may find in the docs of this library.
+:::
+
+### Integration to an application
+
+After installation you need to integrate it to your application. Basically there you have two ways of usage:
+- as JSX-element
+- imperative call
+
+I would **highly** recommend you to capture the startup time on application, that builded with minification and in prod mode (at least without the usage of `dev-server`). In other case, metrics, that you captured may not reflect a real situation.
+
+However in a lot of application the most common used approach is deleting any `console.log` statements in application in production mode via babel-plugin, for example. So I'd recommend you use **JSX-element** approach.
+
+So let's start with JSX-element. First of all you need to add import statement:
+
+```diff
++ import { StartupTime } from 'react-native-startup-time';
+```
+
+And after that somewhere in the root of your application you need to add this part of markup:
+
+```tsx
+
+```
+
+That's all. Now you are ready to see the real startup time. For this, please, build a production release and run it on a real devices on both Platform (iOS/Android).
+
+In order to see the difference between two versions of an application (with usage of `react-native-bundle-splitter` and without) you should capture these metrics on both version of application.
+
+:::tip
+For capturing the real metrics you should kill your application after every running.
+:::
+
+:::info
+For a more revealing result you should try to capture results several (I'd recommend at least five) times. In this case you can see the average startup time.
+:::
+
+## Flipper
+
+Starting from 0.62 `react-native` has `Flipper` integration. If you already use this tool you can add [react-native-performance](https://github.com/oblador/react-native-performance) plugin and capture a lot of metrics.
+
+You can read detailed instructions on how to set it up in github repo. More likely you will be interested only in `runJsBundleStart` and `runJsBundleEnd` performance metrics, but this plugin has support for much more.
+
+In the end you will have visualized results, which should look like the image below:
+
+![Flipper performance plugin](https://user-images.githubusercontent.com/378279/105892056-9f677480-6011-11eb-895a-f3f8653449c8.png)
+
+## Expected results
+
+On various projects you may get different results. And these results depend only on size and complexity of your js code and static assets. And if you were able to reduce your initially loaded bundle from 50 mb to 2 mb, for example, of course, you may expect significant boost of the performance.
+
+From perspective of my practice I get various results, but the average range was about 0.3-1.5s.
+
+As an example I can show you the performance table from the last project, which I was written.
+
+| Platform | Usage of `react-native-bundle-splitter` | Time (ms) | Average |
+|:-------------:|:----------------------------------------:|:----------------------------:|:---------:|
+| **Android** | ✅ | 834, 861, 820, 847, 816 |**835.6** |
+| **Android** | ❌ | 1374, 1325, 1310, 1281, 1354 |**1,328.8**|
+| **iOS** | ✅ | 1074, 1056, 1044, 1068, 1052 |**1,058.8**|
+| **iOS** | ❌ | 1784, 1767, 1798, 1779, 1783 |**1784** |
+
+As you can see the difference in startup time is ~500ms for **Android** and ~700ms for **iOS**.
+
+But it doesn't mean, that for your application results will be the same. Each application and each platform is universal - it has own structure and business logic, so there is only one 100% way to know what is the boost of performance you may get - try to integrate this library to your project and see the results.
diff --git a/docs/versioned_docs/version-3.x/recipes/jest-testing-guide.md b/docs/versioned_docs/version-3.x/recipes/jest-testing-guide.md
new file mode 100644
index 0000000..a8bbe9f
--- /dev/null
+++ b/docs/versioned_docs/version-3.x/recipes/jest-testing-guide.md
@@ -0,0 +1,46 @@
+---
+sidebar_position: 3
+---
+
+# Jest testing guide
+
+This library doesn't require any mocks. However unit tests are running in `Node.js` runtime and dynamic import statement doesn't work out-of-box. To make your tests running again you'll need to add `dynamic-import-node` plugin for `test` environment and modify your `babel.config.js` or `.babelrc` files accordingly.
+
+## Installing new babel dependency
+
+First of all you'll need to install `babel-plugin-dynamic-import-node` to `devDependencies`.
+
+```bash
+yarn add babel-plugin-dynamic-import-node --dev
+# or
+# npm install babel-plugin-dynamic-import-node --save-dev
+```
+
+## Add plugin to babel config
+
+If you are using `babel.config.js` format, then you can add conditional statement to include `dynamic-import-node` plugin only for test environment.
+
+```ts
+// The plugin was added because jest can't use dynamic imports and was changing the imports and
+// final module had following shape { __esModule: true, default: RealModule }.
+// So, react-native-bundle-splitter can't resolve this import format.
+if (process.env.NODE_ENV === "test") {
+ config.plugins.push("dynamic-import-node");
+}
+```
+
+If you are using `JSON` format (i. e. `.babelrc`), then you'll need to add plugin under `env.test` key.
+
+```json
+{
+ "presets": ...
+ "plugins": [
+ ...
+ ],
+ "env": {
+ "test": {
+ "plugins": ["dynamic-import-node"]
+ }
+ }
+}
+```
diff --git a/docs/versioned_sidebars/version-3.x-sidebars.json b/docs/versioned_sidebars/version-3.x-sidebars.json
new file mode 100644
index 0000000..caea0c0
--- /dev/null
+++ b/docs/versioned_sidebars/version-3.x-sidebars.json
@@ -0,0 +1,8 @@
+{
+ "tutorialSidebar": [
+ {
+ "type": "autogenerated",
+ "dirName": "."
+ }
+ ]
+}
diff --git a/docs/versions.json b/docs/versions.json
index fd4e814..17da8b1 100644
--- a/docs/versions.json
+++ b/docs/versions.json
@@ -1,4 +1,5 @@
[
+ "3.x",
"2.x",
"1.x"
]
diff --git a/package.json b/package.json
index da11de9..1e30f82 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "react-native-bundle-splitter",
"author": "Kiryl Ziusko",
- "version": "2.2.3",
+ "version": "3.0.0",
"description": "Decrease your start up time and RAM memory consumption by an application via splitting JS bundle by components and navigation routes",
"repository": "https://github.com/kirillzyusko/react-native-bundle-splitter",
"homepage": "https://kirillzyusko.github.io/react-native-bundle-splitter/",