ReactNative ReactNativeWeb MonoRepo via Lerna

shubhadip maity
4 min readMay 21, 2020

Monorepo is a way through which we can maintain multiple projects in a single repository wherein each project will be isolated from each other.Benefits of having a monorepo are code reuse, dependency management and code visibility across team.

In this post we will be creating a monorepo that will contain a react-native project, a react-native-web project and a common package has code which is used across both native and a web project.

Create MonoRepo:

To create a mono repo we will be using Lerna. Its an tool to manage js projects with multiple packages.

npm install --global lerna
mkdir lerna-repo && cd lerna-repo
lerna init

this will create a directory structure like this

lerna-repo/
packages/
package.json
lerna.json

lets make few changes in lerna.json and package.json

// lerna.json {
"packages": ["packages/*"],
"npmClient": "yarn",
"version": "1.0.1",
"useWorkspaces": true
}

this enables integration with yarn workspaces which allows us to use multiproject dependencies at one-place ( root directory).

Add the following key in package.json

..."workspaces": {
"packages": ["packages/*"],
"nohoist": ["**/react-native", "**/react-native/**"]
...

What is noHoist

So in monorepo workspaces if we run yarn what it does it hoists you dependencies at the root folder. But in case of ReactNativeCli , it will search for react-native in node_modules within the project hence we have to prevent it from hoisting.

now we will create individual projects inside packages folder with following names:

mkdir common nativeapp web

common directory will have code which can be re-used in projects nativeapp and web. whereas nativeapp will have react-native code and web will have code related to react-native-web app

to create react-native app:

react-native init nativeapp --typescript

This will create our reactnative app you can skip the typescript option.lets create other two repos.

mkdir common && cd common
npm init -y
yarn add react react-native react-native-web react-dom
yarn add -D typescript @types/react @types/react-dom @types/react-native
mkdir src && cd src
touch index.tsx
// change the name of repo to monoreporeact

Add a tsconfig.json file for typescript

now we can create a component folder in src which will have our different resuable components in it.Then we can import those components in index.tsx like this which will mark as entry point while transpiling the project.

                                                    // src/index.tsx
// @ts-nocheck
import CustomButton from ‘./CustomButton/index’;
export {
CustomButton
}

so our common project structure would look like this

common folder structure

we also have to change few things in package.json, replace these command from inital values in package.json in common folder.

"name": "monoreporeact",
"main": "./build/index.js",
"scripts": {
"build": "./../../node_modules/typescript/bin/tsc",
"lib": "./../../node_modules/typescript/bin/tsc -w"
},

Now lets move into web project

mkdir web && cd webnpm init -y
yarn add react react-dom react-native react-native-web
yarn add -D @types/react @types/react-dom @types/react-native html-loader ts-loader webpack webpack-cli webpack-dev-server typescript html-webpack-plugin
mkdir public src typings
touch tsconfig.json
touch webpack.config.js
cd src && touch index.tsx App.tsx

lets add content for tsconfig and webpack files.

webpack.config.js
tsconfig.json

lets add index.d.ts inside typings folder with following content :

declare module 'monoreporeact';

contents for index.tsx will be

// index.tsximport { AppRegistry } from "react-native";
import App from "./App"
AppRegistry.registerComponent("App", () => App);
AppRegistry.runApplication("App", { rootTag: document.getElementById("root") });

now will have to create index.html inside public folder for webpack-dev-server

// public/index.html<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="theme-color" content="#000000">
<title>React App</title>
</head>
<body>
<div id="root">
</div>
</body>
</html>

Last Part is to create App.tsx in src folder

import * as React from 'react';
import {Text, View, StyleSheet} from 'react-native';
import { CustomButton } from 'monoreporeact';
const styles = StyleSheet.create({
box: { padding: 10 },
text: { fontWeight: 'bold' }
});
class App extends React.Component {render() {
return (
<View style={styles.box}>
<Text style={styles.text}>Hello, world!</Text>
<CustomButton />
</View>
);
}
}
export default App;

This sums up our web project.

Now that we have completed our setup we will see how can we use our common project in native and web project.we will have to delete node_modules in project

find . -type dir -name node_modules | xargs rm -rf && yarn

now add

"monoreporeact": "^1.0.0"

in dependencies of web and native project in this way we can have our common project in both projects and run yarn you will see that node_modules gets created on root directory and you will see monoreporeact at the root node_modules

Now we can import your common components in both web as well as native project.

import { yourcomponent } from "monoreporeact";

Only Gotcha here is that while developing components in common folder and using it in NativeApp we have add symlink to have realtime updates, But ReactNative doesn’t support symlinks so we have to use wml.

npm install -g wml
wml add ./common ./nativeapp/node_modules/monoreporeact
wml start

And this enables you to have realtime changes will developing reactnativeApp and common components.

at this point our project structure would look like this

project structure mono repo

Github Repo link for this article is available here

--

--