---
title: "Setting up React Native Monorepo With Yarn Workspaces"
description: "Why you should create a React Native monorepo and how to do it."
url: https://www.callstack.com/blog/setting-up-react-native-monorepo-with-yarn-workspaces
updated: 2026-10-24T08:39:36.958Z
---

# Setting up React Native Monorepo With Yarn Workspaces

Why you should create a React Native monorepo and how to do it.

_By Oskar Kwaśniewski, Software Developer @ Callstack · Published 2022-10-11 · Category: Developer Experience_

A monorepo is a single repository that holds a multitude of projects with all their code and assets. While the projects can be related, they can be used independently by different teams. 

Working with monorepos is very useful, especially when developing big and complex applications like [super apps](https://www.callstack.com/blog/why-have-a-super-app). Monorepos enable sharing the logic between a web app and a mobile app, for example.

In this article, we’re going to set up a basic structure for a monorepo in React Native.

## Why use monorepo in React Native?

Thanks to react-native-web we can share every component so there’s no need to duplicate the code many times. This means easier dependency management, shorter CI times, and better collaboration between teams since the code can be shared.

The main advantage though is the possibility of sharing packages between React and React Native. Most of the time we decide to share only business logic, but thanks to React Native Web, we can also share some of the UI components.

## Setting up Yarn workspaces

While setting up a monorepo, we have two options: we can either hoist the packages to the root level or prevent them from hoisting. Yarn workspaces have an option named nohoist which allows us to specify packages that aren’t hoisted. It depends on your use case but most of the time it’s better to hoist packages to the root level.

Alternatively, you can use[ npm workspaces](https://docs.npmjs.com/cli/v7/using-npm/workspaces), it _should_ work the same.

### Why it’s better to avoid using nohoist

Preventing packages from hoisting takes back the main advantage of monorepos which is sharing node\_modules between repositories. When nohoist option is turned on, most of the time packages are duplicated inside the root level node\_modules and inside the project level node\_modules. Downloading packages multiple times can lead to longer CI/CD runs and higher costs.

### Rearranging the project structure

In order to set up yarn workspaces, we need to restructure our project to suit the structure below. Projects need to be divided into separate folders and we should have root level package.json

[GitHub Gist](https://gist.github.com/callstack-bot/153f4e4e15791065fd01dd57cb426730)

Next, you should create a package.json file in the root directory of our project with this command: $ yarn init -y

It should look like this:

[GitHub Gist](https://gist.github.com/callstack-bot/00a07fe6d69f1d9fa9270db86bc3a2a4)

Note that private: true is required because workspaces are not meant to be published.

In the workspaces section, we define a work tree. We can pass there an array of glob patterns that should be used to locate the workspaces. So in the example above, every folder inside packages is defined as a workspace.

### Creating a React Native project

Use command below to create a React Native project inside the packages folder:

[GitHub Gist](https://gist.github.com/callstack-bot/f666e8c6ada815b30444b70e6e1e4e19)

In order for our app to work in a monorepo structure, we need to make sure that config files are pointing to the root level node\_modules due to package hoisting.

#### iOS setup

1. Change paths at the top of your Podfile

[GitHub Gist](https://gist.github.com/callstack-bot/b6f07259a08e8af8f4b678266c724fd4)

1. Regenerate Pods, by entering the command below in the packages/mobile folder:

[GitHub Gist](https://gist.github.com/callstack-bot/dc54172d5b9e2fb78e96b8c0ac95723c)

1. Next open Xcode and inside Project settings \> Build Phases open “Bundle React Native code and images”

Change the path of the script:

[GitHub Gist](https://gist.github.com/callstack-bot/47fa581379143d26ffb8b941d97f6969)

It should look like this:

![XCode Build phases settings](https://www.callstack.com/assets/A%252520screenshot%252520for%252520iOS%252520setup.png)

#### Android setup

React Native ≥ 0.71

Uncomment the line with reactNativeDir and point it to root node\_modules

[GitHub Gist](https://gist.github.com/callstack-bot/959430f8f3dfb5f0ada39058e732c16e)

In top-level build.gradle add this:

[GitHub Gist](https://gist.github.com/callstack-bot/394a81925dab92be914fdfedac3d6bf0)

and inside: packages/mobile/android/settings.gradle

[GitHub Gist](https://gist.github.com/callstack-bot/31ebc8fe1081b3d18d6dda7c5460f946)

‍  
React Native ≤ 0.71

1. Fix paths inside packages/mobile/android/build.gradle

Change url of the React Native Android binaries, and Android JSC in the allprojects section:

[GitHub Gist](https://gist.github.com/callstack-bot/0f6d4ce0be60e41092ed684831768865)

1. Fix packages/mobile/android/settings.gradle

Here we also need to change paths to the root node\_modules folder.

[GitHub Gist](https://gist.github.com/callstack-bot/912dc87a0fcaf5f3b3268ce802aea09d)

1. Next go to packages/mobile/android/app/build.gradle

Inside the project.ext.react we need to add property cliPath so it will override the default location of the cli.

[GitHub Gist](https://gist.github.com/callstack-bot/a138c380342986006c11453c81fa450a)

In the same file, change this line: apply from: "../../node\_modules/react-native/react.gradle" to point to root level node\_modules as well.

[GitHub Gist](https://gist.github.com/callstack-bot/869f2e7ac507c6dbfb753454eccf2cc7)

And also change: inside packages/mobile/android/app/build.gradle

[GitHub Gist](https://gist.github.com/callstack-bot/c49912a600849eaaaa91fb3a0c488707)

If you are using or planning to use the new architecture, we also need to change a few lines in the isNewArchitectureEnabled() if statement\*\*.\*\*

[GitHub Gist](https://gist.github.com/callstack-bot/8f6a7f101afe659c4b4d74f7d8f26531)

#### Configure Metro

We’re almost done with setting up the project. The last thing in the React Native app is to add watchFolders so metro knows where the linked node\_modules are. The shared modules are symlinked by Yarn, and Metro started supporting symlinks from version 0.75.1, effectively being shipped with React Native 0.72. If you want to turn on symlinks right now, you can pass the experimental flag to your metro config:

[GitHub Gist](https://gist.github.com/HannaEsCS/a7a7a6036f3257aeb079b4a4b850925e)

Regardless of whether you are using symlinks, metro still requires us to list symlinked modules in the config, but this might change in the future. You can follow the [discussion on GitHub](https://github.com/facebook/metro/issues/1).

[GitHub Gist](https://gist.github.com/HannaEsCS/b6cff3c0f8e13c97bf5744f768841e87)

## Setting up a shared package

To keep it simple, we’re going to share only one function across React Native and React.

Inside the packages/shared, create a new package.json file with the command:

[GitHub Gist](https://gist.github.com/callstack-bot/7324da94e7408a181f49567fc11e7b14)

The shared library is in typescript, so we need to set up a build step with the typescript compiler (tsc).

The build step uses rimraf which is a small utility for node that allows us to remove a folder, and then build the app with tsc.

postinstall indicates that our shared dependency will build automatically after installing.

[GitHub Gist](https://gist.github.com/callstack-bot/e801f1937a8a0fd1bb88d717de1685ff)

Note: The package name is really important, because it will indicate from where we will import our module.

It’s also important to add all packages and keep the **exact same version** used in a shared project and target projects.

Next we need to configure tsconfig.json where we can define our build settings.

Here is an example config file:

[GitHub Gist](https://gist.github.com/callstack-bot/8d9d72255b264de2fe5982e6875b1ee5)

Next, let’s create an index.ts file inside the packages/shared folder.

[GitHub Gist](https://gist.github.com/callstack-bot/623e6fbe1b8241e730478b6c0b24585d)

Run this command to build the package:

[GitHub Gist](https://gist.github.com/callstack-bot/318d2e7c05bbe7576468a600d3a08355)

Next, we need to add this package as a dependency in our react native project. So inside packages/mobile/package.json add @example-app/shared as a dependency.

Note that the version of the package must match the version specified inside packages/shared/package.json. Previously we defined that @example-app/shared version is 1.0.0.

[GitHub Gist](https://gist.github.com/callstack-bot/854fa05f5f544019f109a0d5b691fd76)

And also inside the metro.config.js

[GitHub Gist](https://gist.github.com/callstack-bot/78f3941f89f05312b9cfcca628b22e86)

Now, run this command in the root directory, so yarn will set up symlinks for your shared package.

[GitHub Gist](https://gist.github.com/callstack-bot/f6872dcef8424f3e30326430a86caf8b)

Right now we should be able to import the add function inside React Native app.

[GitHub Gist](https://gist.github.com/callstack-bot/5bc1162031483af83707c89aa8854e74)

## Setting up a React project

In order to set up a React project, execute the command below inside packages/web:

[GitHub Gist](https://gist.github.com/callstack-bot/288c2cb2f518d623ee6f41a75fc542a5)

Add @example-app/shared dependency:

[GitHub Gist](https://gist.github.com/callstack-bot/59500de36f12feadf226b26473b2433e)

And we can import and use the shared package:

[GitHub Gist](https://gist.github.com/callstack-bot/68fa870f8ea07910622941afe92555a1)

## Sharing UI components with React Native Web

In order to share UI components, we need React Native Web which provides a compatibility layer between React DOM and React Native.

### Installation steps

Install react-native-web:

[GitHub Gist](https://gist.github.com/callstack-bot/72f809a51365c0b37ded819c5690e1cf)

Install babel-plugin-react-native-web

[GitHub Gist](https://gist.github.com/callstack-bot/7cdc04d0036a0a9d4768a7fef7cda4fa)

### **Modify shared package to export React Native component**

First inside packages/shared, create a new file called TestComponent.tsx

[GitHub Gist](https://gist.github.com/callstack-bot/da59241be3b409632424ee47865fe3d6)

Next export it from the index.ts file :

[GitHub Gist](https://gist.github.com/callstack-bot/bb1004a508e5d8ec8f671c27e15a66fe)

Rebuild the shared package inside packages/shared folder:

[GitHub Gist](https://gist.github.com/callstack-bot/f3f267e8708d9a0ef93a75230228b41a)

And now we should be able to use TestComponent inside a React app.

[GitHub Gist](https://gist.github.com/callstack-bot/21edfb919df3d7e480d2ff919aa555b7)

## To sum up

Setting up a monorepo can be tricky, but it pays off in the long run! You can easily share all of your code and assets between React and React Native.

Using a monorepo is growing in popularity - there is even an open proposal to [migrate react-native repository to monorepo](https://github.com/react-native-community/discussions-and-proposals/pull/480). Find out more about [React Native monorepo](https://github.com/okwasniewski/react-native-monorepo) on github.
