Creating a TypeScript monorepo for React and React Native with Yarn Workspaces (and optionally Webpack)

Introduction

Creating a Yarn monorepo that works well with React and React Native can be challenging and isn’t very well documented. This guide and the accompanying repository (typescript-yarn-monorepo) can help you set up such a monorepo and ultimately improve your productivity.

Example Monorepo Architecture

To demonstrate a cross-platform React and React Native app with shared components, this guide uses the following architecture and directory structure:

Guide Organization

This guide is composed of three parts, each building on the previous part and mapping to a corresponding branch in the repository:

  1. TypeScript React and React Native monorepo with Yarn Workspaces: use-yarn-nohoist
  2. Using Webpack for bundling components: use-webpack
  3. Using Paths with TypeScript and Webpack use-webpack-with-paths

TypeScript React and React Native monorepo with Yarn Workspaces

Steps

{
    "compilerOptions": {
        ...
-       // "lib": [],
+       "lib": [
+           "es6",
+           "dom"
+       ],
        ...
    }
}
{
    "compilerOptions": {
        ...
-       // "sourceMap": true,
+       "sourceMap": true,
        ...
    }
}
{
    "compilerOptions": {
        ...
-       "noUnusedLocals": true,
-       "noUnusedParameters": true,
-       "noImplicitReturns": true,
-       "noFallthroughCasesInSwitch": true,
+       "noUnusedLocals": true,
+       "noUnusedParameters": true,
+       "noImplicitReturns": true,
+       "noFallthroughCasesInSwitch": true,
        ...
    }
}
{
    "compilerOptions": {
        ...
-       // "outDir": "./",
+       "outDir": "./dist",
        ...
    }
}
{
    "compilerOptions": {
        ...
-       // "rootDir": "./",
+       "rootDir": "./src",
        ...
    }
}
{
    "compilerOptions": {
        ...
-       // "moduleResolution": "node",
+       "moduleResolution": "node",
        ...
    }
}
{
    "compilerOptions": {
        ...
+       "skipLibCheck": true,
        ...
    }
}
export interface IExampleService {
    doExampleWork(input: string): Uint8Array;
}
import { IExampleService } from "./IExampleService";

export class BasicExampleService implements IExampleService {
    doExampleWork(input: string): Uint8Array {
        let result: Uint8Array = new Uint8Array(input.length);
        for (let i: number = 0; i < input.length; i++) {
            result[i] = input.charCodeAt(i);
        }

        return result;
    }
}
export * from "./IExampleService";
export * from "./BasicExampleService";
{
    ...
    "version": "1.0.0",
-   "main": "index.js",
+   "main": "dist/index.js",
    "author": ...
    ...
}
{
    "compilerOptions": {
        ...
-       // "declaration": true
+       "declaration": true,
        ...
    }
}
{
    ...
    "main": "index.js",
+   "typings": "dist/index",
    "author": ...
    ...
}
src/
-root/
    -packages/
        -core/
            -src/
                -IExampleService.ts
                -BasicExampleService.ts
            -dist/
                -BasicExampleService.d.ts
                -BasicExampleService.js
                -BasicExampleService.js.map
                -IExampleService.d.ts
                -IExampleService.js
                -IExampleService.js.map
                -index.d.ts
                -index.js
                -index.js.map
            -node_modules/
            -.npmrc
            -package.json
            -tsconfig.json

Repository Notes