Reily app

Project structure with react native and firebase

☕️ 3 min read

If one team works on both react-native client and backend API, we usually prefer to have both codebases in one repo.

This way, one pull request and have the complete code changes for one feature.

This is especially the case when using firebase as backend.

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": [ "app", "functions", "web", "scripts" ]

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

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

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": [ "**" ]  },

Read more

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
Edit on GitHub