Project structure with react native and firebase

If one team works on both react-native client and backend API, have both codebases in one repo can help reduce cluster, one pull request can have the complete code changes for one feature.

yarn workspace

To put them in the same repo, the easiest way is to put them in two separate, isolated folders:

  • /client: the react native app
  • /firebase: rest of the stuff

But we can do better with yarn workspace, here is why.

	"private": true,
  "workspaces": {
    "packages": [

Unfortunately, react-native does not work well with mono workspace out of the box.

Unable to use react-native link

Currently react-native link would look for packages in /app/node_modules and fail, because it’s actually in /node_modules. Thus we have do manual integrations.

Native library search path

Most iOS lib for react-native hardcoded Header/Framework search paths,

For example, in react-native-firebase, search path look like following


<img width=“100%” style={{borderRadius:10, marginBottom: 10}} src={require(‘./react-native-firebase.jpg’)} alt=“React Native Firebase Search Path” />

It assumes node_modules is located at app/node_modules, the same folder where .xcodeproject is. So obviously it will not able be to found these frameworks.

Same thing goes for header file search paths.

To fix this issue, you can:

  1. patch-package all the native packages’ search path.
  2. nohoist all native packages. (We will talk more about nohoist in just a bit).
  3. Create a system file link, from app/ios to ios.

This issue does not apply to android, but on Android, you still need to adjust the path you import in settings.gradle

jsBundle location

react-native wasn’t built to customize jsBundle location, which is why [@brunolemos]( created this patch on iOS and this patch on android for devhub.

These issues are hard to fix and also really hacky, and when react-native upgrades, they might break and require a manual upgrade.

So, nohoist to the rescue.

  "name": "app",
  "private": true,
  "workspaces": {
    "nohoist": [ "**" ]

husky, lint-staged, prettier

We yarn workspace, it also bring lots of benefits. For one, now we only need to configure husky, lint-staged, prettier only once on workspace root.

  "devDependencies": {
    "husky": "^1.3.1",
    "lint-staged": "^8.1.4",
    "prettier": "^1.16.4"
  "husky": {
    "hooks": {
      "pre-commit": "lint-staged",
      "post-commit": "git update-index -g"
  "lint-staged": {
    "*.{js,json,md,ts,tsx}": [
      "prettier --write",
      "git add"

It will keep all the code lint and pretty formatted, and more importantly consistent between all the sub-projects

Share Configuration

Just like husky, lint-staged, prettier we can unify tslint.json and tsconfig.json.

We can have share/common setting in the project root, and then in each subproject.

  "extends": "../../tsconfig.json",
  "extends": ["../../tslint.json"],

Share UI modal

if web were to be built with react-native-web, then we can have another workspace common.

We can build UI and then import it from both app and web.


The scripts subproject is meant for project house cleaning, for example, we might need a script to add admin privileges to a certain user. And we can keep the script around for later use or references.

Here are some of the really useful tools to help you with this task:

  • meow CLI app helper
  • chalk Terminal string styling done right
  • ora Elegant terminal spinner
  • ts-node TypeScript execution and REPL for node.js