Lang
Blog

React Native, React Web and Expo-Together in One Monorepo

ByShankar Morwal
March 11th . 5 min read

I am working on a project where we have a React web app, React Native app, and another React Native app which is built using expo. All these three projects share a common backend, service layer, and redux logic.

There are some other common components as well that can be used across react native and expo app.

So, we needed an architecture to avoid duplicate code across the projects. I stopped for a moment and thought, what could be better than the monorepo architecture? It will also solve the problem stated above.

The Monorepo architecture provides the leverage to reuse much of the service layer, redux related code, and other shareable pieces of code across the components.

In the tutorial below, I will show step by step how can you use react web, react native, and react native Expo project in one monorepo.

Let’s check out what exactly is a Monorepo??

Monorepo??

As stated by Wikipedia-

A monorepo, syllabic abbreviation of monolithic repository, is a software development strategy where code for many projects is stored in the same repository.

react-native-react-web_1.jpg

In simple words, monorepo contains more than one project code, which allows us to share code between many projects. Each project can have its own build and deployment system or can share build systems.

Many companies, including Google, Twitter, Facebook, etc. follow this process.

Now, I will discuss all the things that we used, the challenges we faced, and resolved during this journey.

You can also take the help of the demo repository I created. (Link is given at the end of this tutorial).

Getting Started with Monorepo for React and React-Native

We decided to use yarn-workspaces for our use-case. Lerna is an excellent tool to manage yarn workspaces.

As described on the Lerna website about itself -

Lerna is a tool that optimize the workflow around managing multi-package repositories with git and npm.

To use Lerna, we will first need to install it. I am assuming you’ve already had Node.js and npm installed in your system.

#Installing Lerna 2.x
$ npm install --global lerna
#Creating project repository
$ git init lerna-repo && cd lerna-repo
#Turning folder into Lerna workspace
$ lerna init

Your repository will look like-

lerna-repo/
  packages/
  package.json
  lerna.json

Now, you are ready with Lerna Workspace. Here, the packages, as per the name dictates, will contain different modules.

Let’s start adding a React js project into packages-

Create React App into Monorepo

#Creating app in packages folder
$ npx create-react-app <app-name>
#Example
$ npx create-react-app web-app

Please note that NPX supports only npm 5.2+ and higher versions, so if you have a lower version then you definitely need to upgrade it.

Once it is completed, go to the root folder of your repository and run-

$ yarn install

All the required modules will be installed and hoisted to the root folder by this command.

To start the web application-

$ lerna --scope=web-app run start

Congrats!! You are done with your first package.

Add React Native App Package

Go to the packages folder and run the following command-

$ npx react-native init <App name>

That is,

$ npx react-native init NativeApp

It will create all the folders and files required for the native app.

Once the process gets completed, go to the root folder and install packages again-

$ yarn

It will create React Native and other dependencies to root in npm_modules of monorepo. However, create-react-native-cli command expects react-native to be inside the package. If we still try to run it, there will be an error.

To fix this issue, we will use the yarn-workspaces nohoist feature.

In workspaces key of JSON package, we will add-

"nohoist": ["**/react-native", "**/react-native/**"]

Now, we’re gonna install packages again using yarn command.

$ yarn

Then we can start the apps using the following commands.

#Starting the android app
lerna --scope=NativeApp run android
#Start iOS app
lerna --scope=NativeApp run ios

Please note that an iOS app will work in MacBook only.

Well, now you have a repository that has both React web app and React Native mobile apps.

As we move further, we will try to add a React-Native expo project in this Monorepo as another package. If you don’t need an expo project, then you’re done here. You can stop right here!

How to Make Expo Work with Monorepos??

  1. Add expo-yarn workspaces as devDependency to the expo project, inside the ExpoApp folder.
$ yarn add --dev expo-yarn-workspaces
  1. Add a postinstall script to package.json.

  2. Add "postinstall": "expo-yarn-workspaces postinstall" under the "scripts" object in the app's package.json file.

As per the documentation, expo-yarn-workspaces work only on macOS and UNIX based systems, Windows is not supported.

If we want it to work on Windows as well, then we need to make slight modifications to it.

Firstly, remove the postinstallscript from package.json and move it to the main package.json-

"postinstall": "cd ./packages/ExpoApp && expo-yarn-workspaces postinstall"

I tested the above workaround on macOS & Windows, and guess what?? It works like charm.

  1. Now, define the entry module in the "main" field of package.json.

  2. Specify "generated/AppEntry.js" as the value of the "main" field in package.json

Here is how the final package.json will look like-

{
  "name": "root",
  "private": true,
  "devDependencies": {
    "lerna": "^3.20.2"
  },
  "dependencies": {
    "metro-config": "^0.56.0"
  },
  "scripts": {
    "android": "lerna --scope=NativeApp run android --stream --",
    "ios": "lerna --scope=NativeApp run ios --stream --",
    "web": "lerna --scope=web-app run start --stream --",
    "expo": "lerna --scope=ExpoApp run start --stream --",
    "postinstall": "cd ./packages/ExpoApp && expo-yarn-workspaces postinstall"
  },
  "workspaces": {
    "packages": [
      "packages/*"
    ],
    "nohoist": ["**/react-native", "**/react-native/**", "**/expo", "**/expo/**"]
  }
}
  1. Create metro.config.js in ExpoApp root folder with the following content-
const { createMetroConfiguration } = require('expo-yarn-workspaces');
module.exports = createMetroConfiguration(__dirname);
  1. Reference metro.config.js in the app.json file inside the expo key.
"packagerOpts": {
  "config": "metro.config.js"
}

Now, go to the root folder and install packages again, using $ yarn .

#Start expo app
$ lerna --scope=ExpoApp run start

It will successfully start the expo app. But when you scan the app code on it, you will get an error as ‘react-native not found’.

As we said earlier that React Native should not be hoisted but the expo package is still hoisted. As a result, expo cannot find React-Native.

So, we need to update the hoisting of packages inside the package.json file.

"nohoist": ["**/react-native", "**/react-native/**", "**/expo", "**/expo/**"]

After updating the package.json file with proper hoisting, we need to install the packages again using $ yarn .

Now try starting the expo project again-

$ lerna --scope=ExpoApp run start

Wow!! You’ll see that the Expo app has started working inside the monorepo environment.

Together with the expo project, you can run the React web app and React Native projects as well.

Here is the full folder structure-

react-native-react-web_2.jpg

You will find the full code example here.

All the code is quite simple and straightforward. I hope I’ve made every step very clear. Furthermore, if you have got any queries, feel free to reach out. If you are looking for a team for react or react-native then please contact us.

If you liked the blog then hit claps to it. Do you know that you can clap up to 50+ times??

Share:
0
+0