Skip to content

Commit 891fdbc

Browse files
authored
Extract Module to Library (facebook#4274)
* Remove video if too big * Readd video * Add base info for Android * Add Android integration * [WIP] Begin work on the extracting a module to a library * Finalize the guide * Review and finalize the guide * Apply review suggestion * Fix build and prettier * Add local module setup
1 parent 5ca591e commit 891fdbc

File tree

3 files changed

+195
-1
lines changed

3 files changed

+195
-1
lines changed
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
# Create a Library for Your Module
2+
3+
React Native has a florid ecosystem of libraries to solve common problems. We collect React Native libraries in the [reactnative.directory](https://reactnative.directory) website, and this is a great resource to bookmark for every React Native developer.
4+
5+
Sometimes, you might be working on a module that is worth extracting in a separate library for code reuse. This can be a library that you want to reuse in all your apps, a library that you want to distribute to the ecosystem as an open source component, or even a library you'd like to sell.
6+
7+
In this guide, you'll learn:
8+
9+
- how to extract a module into a library
10+
- how to distribute the library using NPM
11+
12+
## Extract the Module into a Library
13+
14+
You can use the [`create-react-native-library`](https://callstack.github.io/react-native-builder-bob/create) tool to create a new library. This tool sets up a new library with all the boilerplate code that is needed: all the configuration files and all files required by the various platforms. It also comes with a nice interactive menu to guide you through the creation of the library.
15+
16+
To extract a module into a separate library, you can follow these steps:
17+
18+
1. Create the new library
19+
2. Move the code from the App to the Library
20+
3. Update the code to reflect the new structure
21+
4. Publish it.
22+
23+
### 1. Create a Library
24+
25+
1. Start the creation process by running the command:
26+
27+
```sh
28+
npx create-react-native-library@latest <Name of Your Library>
29+
```
30+
31+
2. Add a name for your module. It must be a valid npm name, so it should be all lowercase. You can use `-` to separate words.
32+
3. Add a description for the package.
33+
4. Continue filling the form until you reach the question _"What type of library do you want to develop?"_
34+
![What type of Library](/docs/assets/what-library.png)
35+
5. For the sake of this guide, select the _Turbo module_ option. Notice that you can create libraries for both New Architecture and Legacy Architecture.
36+
6. Then, you can choose whether you want a library that access the platform (Kotlin & Objective-C) or a sahred C++ library (C++ for Android and iOS).
37+
7. Finally, select the `Test App` as last option. This option creates the library with a separate app already configured within the library folder.
38+
39+
Once the interactive prompt is done, the tool creates a folder whose structure looks like this in Visual Studio Code:
40+
41+
![C++VisualStudio](/docs/assets/c++visualstudio code)
42+
43+
Feel free to explore the code that has been created for you. However, the most important parts:
44+
45+
- The `android` folder: this is where the Android code lives
46+
- The `cpp` folder: this is where we the c++ code lives
47+
- The `ios` folder: this is where we the iOS code lives
48+
- The `src` forder: this is where the JS code lives.
49+
50+
The `package.json` is already configured with all the information that we provided to the `create-react-native-library` tool, including the name and the description of the package. Notice that the `package.json` is also already configured to run codegen.
51+
52+
```json
53+
"codegenConfig": {
54+
"name": "RN<your module name>Spec",
55+
"type": "all",
56+
"jsSrcsDir": "src",
57+
"outputDir": {
58+
"ios": "ios/generated",
59+
"android": "android/generated"
60+
},
61+
"android": {
62+
"javaPackageName": "com.<name-of-the-module>"
63+
}
64+
},
65+
```
66+
67+
Finally the library contains already all the infrastruction to let the library be linked with iOS and Android.
68+
69+
### 2. Copy the Code over from Your App
70+
71+
The rest of the guide assumes that you have a local Turbo Native Module in your app, created following the guidelines shown in the other guides in the website: platform specific Turbo Native Modules, or [cross-platform Turbo Native Modules](./pure-cxx-modules). But it works also for Components and legacy architecture modules and components. You'll have to adapt the files you need to copy and update.
72+
73+
<!-- TODO: add links for Turbo Native Modules -->
74+
75+
1. **[Not required for legacy architecture modules and components]** Move the code you have in the `specs` folder in your app into the `src` folder created by the `create-react-native-library` folder.
76+
2. Update the `index.ts` file to properly export the Turbo Native Module spec so that it is accessible from the library. For example:
77+
78+
```ts
79+
import NativeSampleModule from './NativeSampleModule';
80+
81+
export default NativeSampleModule;
82+
```
83+
84+
3. Copy the native module over:
85+
86+
- Replace the code in the `android/src/main/java/com/<name-of-the-module>` with the code you wrote in the app for your native module, if any.
87+
- Replace the code in the `ios` folder with the code you wrote in your app for your native module, if any.
88+
- Replace the code in the `cpp` folder with the code you wrote in your app for your native module, if any.
89+
90+
4. **[Not required for legacy architecture modules and components]** Update all the references from the previous spec name to the new spec name, the one that is defined in the `codegenConfig` field of the library's `package.json`. For example, if in the app `package.json` you set `AppSpecs` as `codegenConfig.name` and in the library it is called `RNNativeSampleModuleSpec`, you have to replace every occurrence of `AppSpecs` with `RNNativeSampleModuleSpec`.
91+
92+
That's it! You have moved all the required code out of your app and in a separate library.
93+
94+
## Testing your Library
95+
96+
The `create-react-native-library` comes with an useful example application that is already configured to work properly with the library. This is a great way to test it!
97+
98+
If you look at the `example` folder, you can find the same structure of a new React Native application that you can create from the [`react-native-community/template`](https://github.com/react-native-community/template).
99+
100+
To test your library:
101+
102+
1. Navigate to the `example` folder.
103+
2. Run `yarn install` to install all the dependencies.
104+
3. For iOS only, you need to install CocoaPods: `cd ios && pod install`.
105+
4. Build and run Android with `yarn android` from the `example` folder.
106+
5. Build and run iOS with `yarn ios` from the `example` folder.
107+
108+
## Use your library as a Local Module.
109+
110+
There are some scenario where you might want to reuse your library as a local module for your applications, without publishing it to NPM.
111+
112+
In this case, you might end up in a scenario where you have your library sitting as a sibling of your apps.
113+
114+
```shell
115+
Development
116+
├── App
117+
└── Library
118+
```
119+
120+
You can use the library created with `create-react-native-library` also in this case.
121+
122+
1. add you library to your app by navigating into the `App` folder and running `yarn add ../Library`.
123+
2. For iOS only, navigate in the `App/ios` folder and run `bundle exec pod install` to install your dependencies.
124+
3. Update the `App.tsx` code to import the code in your library. For example:
125+
126+
```tsx
127+
import NativeSampleModule from '../Library/src/index';
128+
```
129+
130+
If you run your app right now, Metro would not find the JS files that it needs to serve to the app. That's because metro will be running starting from the `App` folder and it would not have access to the JS files located in the `Library` folder. To fix this, let's update the `metro.config.js` file as it follows
131+
132+
```diff
133+
const {getDefaultConfig, mergeConfig} = require('@react-native/metro-config');
134+
135+
/**
136+
* Metro configuration
137+
* https://reactnative.dev/docs/metro
138+
*
139+
* @type {import('metro-config').MetroConfig}
140+
*/
141+
+ const path = require('path');
142+
143+
- const config = {}
144+
+ const config = {
145+
+ // Make Metro able to resolve required external dependencies
146+
+ watchFolders: [
147+
+ path.resolve(__dirname, '../Library'),
148+
+ ],
149+
+ resolver: {
150+
+ extraNodeModules: {
151+
+ 'react-native': path.resolve(__dirname, 'node_modules/react-native'),
152+
+ },
153+
+ },
154+
+};
155+
156+
module.exports = mergeConfig(getDefaultConfig(__dirname), config);
157+
```
158+
159+
The `watchFolders` configs tells Metro to watch for files and changes in some additional paths, in this case to the `../Library` path, which contains the `src/index` file you need.
160+
The `resolver`property is required to feed to the library the React Native code used by the app. The library might refer and import code from React Native: without the additional resolver, the imports in the library will fail.
161+
162+
At this point, you can build and run your app as usual:
163+
164+
- Build and run Android with `yarn android` from the `example` folder.
165+
- Build and run iOS with `yarn ios` from the `example` folder.
166+
167+
## Publish the Library on NPM.
168+
169+
The setup to publish everything on NPM is already in place, thanks to `create-react-native-library`.
170+
171+
1. Install the dependencies in your module `yarn install`.
172+
2. Build the library running `yarn prepare`.
173+
3. Release it with `yarn release`.
174+
175+
After a while, you'll find your library on NPM. To verify that, run:
176+
177+
```bash
178+
npm view <package.name>
179+
```
180+
181+
where `package.name` is the `name` you set up in the `package.json` file during the initialization of the library.
182+
183+
Now, you can install the library in your application by running:
184+
185+
```bash
186+
yarn add <package.name>
187+
```
188+
189+
:::note
190+
For iOS only, whenever you install a new module with some native code, you have to reinstall CocoaPods, by running `bundle exec pod install` (recommended) or `pod install` if you are not using Ruby's Bundler (not recommended).
191+
:::
192+
193+
Congratulations! You published your first React Native library.

website/sidebars.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,8 @@
8383
],
8484
"Native Modules": [
8585
"the-new-architecture/pure-cxx-modules",
86-
"the-new-architecture/custom-cxx-types"
86+
"the-new-architecture/custom-cxx-types",
87+
"the-new-architecture/create-module-library"
8788
],
8889
"Android and iOS guides": [
8990
{
433 KB
Loading

0 commit comments

Comments
 (0)