|
| 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 | +  |
| 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 | + |
| 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. |
0 commit comments