The goal of this monorepo is to provide an unopinionated, zero-config monorepo setup for the Expo app template generated by yarn create expo
so you can get started up and running as quickly as possible!
With first-class support from Expo, starting with Expo SDK 48, yarn is arguably the easiest way to manage a super simple monorepo, play around with Expo, and graduate to a more complex monorepo setup later.
yarn install
yarn start
From this point on, the rest of the documentation found below is dedicated to additional context, references, recreation steps, and issues you may encounter. Good luck!
In this example, we will set up a monorepo using Yarn workspaces without the nohoist option.
-
Make sure you have yarn 1.x installed.
yarn --version
Install it if you don't have it.
npm install --global yarn
-
Initialize yarn project. Run
yarn init
, or create the package.json manually. It should look something like this:Note: All Yarn monorepos should have a "root" package.json file.
{ "name": "monorepo", "version": "1.0.0" }
-
Set up yarn workspaces. Yarn and other tooling have a concept called "workspaces". Every package and app in our repository has its own workspace. Before we can use them, we have to instruct Yarn where to find these workspaces. We can do that by setting the workspaces property using glob patterns, within package.json:
Note: Yarn workspaces require the root package.json to be private.
{ "name": "monorepo", "version": "1.0.0", "workspaces": ["apps/*", "packages/*"], "private": true }
-
Create our first app.
a. Create the app directory.
mkdir apps/cool-app cd apps/cool-app
b. Create the Expo app with its default
tabs
template using Expo Router.yarn create expo apps/cool-app
c. Make sure to move the
.gitignore
file to the root of the monorepo.mv apps/cool-app/.gitignore ./
-
Now test the app! When running the
npx expo start
command, use theEXPO_USE_METRO_WORKSPACE_ROOT
environment variable. It enables the auto server root detection for Metro. This variable can also be defined inside a.env
file. After testing, it appears that this isn't always necessary.Note: If you are not using Expo Router, you can change the default entrypoint.
a. When testing the app, you should see the following message:
▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ █ ▄▄▄▄▄ █▀▄█▀ ▄█ ▄█ ▄▄▄▄▄ █ █ █ █ █▀▄▀ ▀▀▄ ▄▄ █▀█▄█ █ █▄▄▄█ █▀▀▄ ▀ █ █▄ ▄█▄█ █▄▄▄▄▄▄▄█▄▄▄▄▄█▄█▄███▄▄█▄▄█ › Metro waiting on exp://192.168.1.19:8082 › Scan the QR code above with Expo Go (Android) or the Camera app (iOS) › Web is waiting on http://localhost:8082 › Using Expo Go › Press s │ switch to development build › Press a │ open Android › Press i │ open iOS simulator › Press w │ open web
b. Make certain
live reload
is working as expected by testing both the web (w) and native (i | a) apps. You can do this by starting those apps and making changes to theHomeScreen
component defined within index.tsx. Changes should immediate. -
Create a package.
a. From within the root of the monorepo, create the package directory and initialize it with yarn.
mkdir -p packages/cool-package cd packages/cool-package yarn init
b. Add a simple
index.js
file to the package.echo "export const greeting = 'Hello!';" > index.js
-
Add the package to the app.
a. Add
cool-package
to apps/cool-app/package.json so we can use it within our Expo app.yarn add cool-package
Note: Like standard packages, we need to add our cool-package as a dependency to our cool-app. The main difference between a standard package, and one from the monorepo, is you'll always want to use the "current state of the package" instead of a version. Let's add cool-package to our app by adding "cool-package": "*" to our app package.json file.
b. After adding the package as a dependency, run yarn install to install or link the dependency to your app.
yarn install
c. You should now be able to use the package inside your app! To test this, let's edit the
HomeScreen
component defined within index.tsx again:import { greeting } from "cool-package"; // ... <ThemedView style={styles.titleContainer}> <ThemedText type="title">{greeting}</ThemedText> <HelloWave /> </ThemedView>; // ...
d. When running the apps, you should now see
Hello!
displayed instead ofWelcome!
. -
Make life easier by adding some root package scripts.
a. Add the following to the package.json file:
"scripts": { "start": "yarn workspace cool-app start", "start:android": "yarn workspace cool-app android", "start:ios": "yarn workspace cool-app ios", "start:web": "yarn workspace cool-app web" }
b. Run with:
yarn start # or yarn start:android yarn start:ios yarn start:web
Error: Unable to resolve "../../App" from "node_modules/expo/AppEntry.js"
Note: as seen when running
npx expo start
Problem: you're likely trying to run the app from the wrong location (e.g. the root of the monorepo).
Solution: run the command from the apps/cool-app
directory instead.
More details:
- With an Expo monorepo comes added complexity and issues you'll need to solve along the way, so proceed with caution and patience.
- Expo SDK 50 and higher has improved support for more complete node_modules patterns, such as isolated modules. Unfortunately, React Native can still cause issues when installing multiple versions inside a single monorepo. Because of that, it's recommended to only use a single version of React Native. You can check if your monorepo has multiple React Native versions and why they are installed through the package manager you use.
yarn why react-native
We will assume some familiar names, but you can fully customize them. After this guide, our basic structure should look like this:
apps
- Contains multiple projects, including React Native apps.packages
- Contains different packages used by our apps.package.json
- Root package file, containing Yarn workspaces config.