I can list down the reasons for “why” to do in-house, but I’ll refrain. Since you are landing here I assume you will know your why’s. In my research and finally getting the first iteration out, I find the details and how to part is what makes it worthy to share out to the community. (TLDR;) a is required? Why monorepo to choose? Which monorepo tool bootstrapping (packaging), linting, development workflow (Storybook), testing, publishing. I’ll be using React as the base view library. The setup — Let’s start with the first one. Why a monorepo? Monorepo is a repository with multiple packages. The real question we must be asking is why multiple packages in a single repository? It gives us a chance to have common linting setup 🛠 static type-checking ✅ development workflow ( depends ) 📕 the build system ( depends ) 📦 testing setup ( depends ) 🐛 dependency management ( package.json ) 👲 publishing framework ( automatic semver ) 🚀 Did you notice the “ mention against a few items? Assume we are setting up a web and a native package both in the same repository. It cannot have the same build process by virtue of their platforms. Native would take a different route altogether to compile stuff to their respective IOS and android code. depends” The testing for DOM and testing for native code can be supported by different tools/packages in the market. Development workflow or the boilerplate used for source code is mostly going to be different. Now, most of these points point to . Be it in the form of tooling or project utilities. Tagged along with this is Imagine the effort saved to hop up to different windows of projects and find everything inside one single repository. code reusability code discoverability. Now, if I have to pull up more fancy terms is the other one. Having a thing built to release cycle falls under the agile cycle. Now, imagine the situation with multiple repositories and the process cycle being followed for each one of them for almost relatable things to be pushed out. developer agility Painful! Isn’t it with multiple repositories? Standard setup — Multiple repositories Just imagine a common repository for these multiple packages. Can you figure out from the image below what’s common, what’s eased out? Even if you haven’t continue reading. LINT and BUILD scripts at the root of the repo — Monorepo makes all this possible. It allows us to have multiple projects (or packages ) in the same repository. Of course, these are all pros. Cons that I can think of are increased pressure on linter for all these mini project folders, ever-growing git history object, increased time for build process as the number of projects and their source code grows. Cons Is that overwhelming? It certainly was for me when I started. This is only going to get beautiful though. We will discover the topics in details as we go. I feel this must give a solid base to make a decision if a monorepo is the way to go for you. Moving on… Spoiler — (Yarn Workspaces + Lerna) Which monorepo to choose? Before yarn workspaces After yarn workspaces There are a few available in the market for package management and orchestration. The popular names that strike my head are Bazel, Lerna, Yarn workspaces, rush, pnpm. I’m going to cut this section short and share my reference right away which made the decision easier for me back then. And having researched other sources, plus seems a courting. It has proven well to claims. here yarn workspaces lerna — Lerna will be a and taking care of of packages. yarn workspaces will be used for and . Summary publishing tool semantic versioning package management local symlinking Monorepo setup — Bootstrapping Lerna and Yarn workspaces $ mkdir design-system $ design-system $ npx lerna init cd You will see two files and created along with the empty folder packages. lerna.json package.json Let’s incorporate . yarn workspaces { : [ ], : , : , : } "packages" "packages/*" "version" "independent" "npmClient" "yarn" "useWorkspaces" true The important change to notice here is the version set to This ensures that all our packages follow their . independent semantic versioning Further, we need to tell our to locate yarn workspaces. package.json { : , : , : [ ], : { : } } "name" "root" "private" true "workspaces" "packages/*" "devDependencies" "lerna" "^3.18.4" Linting and transpilation — Typescript Superset of Javascript Typescript is a static type-checker and comes with almost always separate integrations of existing packages, tools. It has been a pain to set up and use initially. But I’ve come to terms. I would say worthy of my time in the end. If you’re new to the decision of Typescript I urge you to spin up a separate research thread for the benefits of it and resume later exactly here. I strongly support the usage of it. We will be using package for linting support. typescript-eslin t Typescript will be used for our code to specified ECMA script version. Yes. for it! transpiling No babel Dependencies we are coming. Let’s get this over with. yarn yarn add react react-dom typescript prettier eslint @typescript-eslint/eslint-plugin @typescript-eslint/parser eslint-config-react eslint-config-prettier eslint-plugin-prettier eslint-plugin-react eslint-plugin-react-hooks The first command is short for . Now, is a good time to create a yarn install .gitignore If you notice the above dependencies, mainly I’ve installed react, typescript, eslint and prettier. Rest is their related packages. Add tsconfig.json { : { : , : , : [ , , ], : , : , : , : , : , : [], : , : , : , : , : , : , : , : , : , : } } "compilerOptions" "target" "es5" "module" "commonjs" "lib" "dom" "dom.iterable" "esnext" "rootDir" "packages" "baseUrl" "." "strict" true "esModuleInterop" true "jsx" "react" "types" "forceConsistentCasingInFileNames" true "strictBindCallApply" true "strictFunctionTypes" true "strictNullChecks" true "noUnusedLocals" true "noImplicitReturns" true "sourceMap" true "skipLibCheck" true "declaration" true "outDir" "dist" Let’s add some packages and source code to test if our basic linting works well. I’m going to create two packages ui-core and ui-components in our setup. Inside ui-components create a file - with below code and you will see linter yell. button.tsx Now, is typescript’s way of telling us that you haven’t typed anything for the button props. ts(1110) Next, create a folder button and moved inside it. Also, I want you to focus on the attempted typing of i.e. event. When I type React. how about a suggestion list from typescript or in this case React object itself. button.tsx e Now, to follow when working with typescript. For every dependency to use. There is gotta have for it. That’s the ONLY way of knowing if a package is typed. a golden rule type definitions These type definitions can either be available within the package itself (if the authors have used typescript in their development ) else there is a website where we can get type for almost all popular packages. https://definitelytyped.org/ So run the following. yarn -W /react /react-dom add @types @types After this, if you observe the same line and try completing the React. type definition it will give a list of suggestions to choose from. Completed button.tsx here I know you’re wondering what that verbose syntax is about? But I’ll leave the Typescript learning to you. Linting out of way, let’s write some more code to help us verify the build process via tsconfig file. Transpilation Add one file to another package ui-core that we created earlier. colors.ts Something as simple as this would work. COLORS { PRIMARY = , PRIMARY_LIGHT = } export enum "#00a465" "#0fb877" Let’s add a script in package.json and run yarn run build : "build" "tsc -p ./tsconfig.json" This will generate a dist folder at the root level for us containing the mirror hierarchy of our package folder. Typescript compiler compiled the code successfully to specified in target version tsconfig.json But this not what we want. Do we? We want the distribution to be separate and god knows, if we are working with a native project, the build process has to be separate too. So single is not going to serve our monorepo needs. tsconfig.json Let’s add two copies of and place them in each of the packages. You can copy these two from the links and tsconfig.json ui-components ui-core Final root . tsconfig.json here I have deleted the entries for and from root outDir declaration tsconfig.json Since I’m mentioning declaration, entry generates the type definitions for your package. Since these are not needed anymore in the root, we can eliminate it. Also, if you’re wondering why to keep the root , it’s because we still need common linting to work for both of our packages. I have to come back to on details for linting even further that I left out earlier. declaration:true tsconfig.json Let’s add this script in our and run package.json yarn run build : "build" "yarn workspace ui-components tsc && yarn workspace ui-core tsc" You will see two dist folders created inside the root of each package. is a command which can be run across individual packages. In this case, it combines two commands for each package. Important to add that there is a better way to achieve this i.e. without specifying each of the packages. yarn workspace "build": "lerna exec - - parallel - - rm - rf dist && lerna exec - - parallel - - tsc - p . /tsconfig . json" Unfortunately, the above failed for me while I’m myself doing the setup on a fresh project. On the final project shared above, it works just fine. So if it does for you, I would suggest you pick up the latter command for building your multiple packages. Well well, our build process is set up. Let me touch the left out portion for linting. We talked about using typescript/eslint but we didn’t really configure it in the project. You will need to add two files to the root of the project and . Also, it is a good time to add the much required package.json to each of the packages. .eslint .eslintignore { : , : } "name" "ui-components" "version" "0.0.0" and { : , : } "name" "ui-core" "version" "0.0.0" Both of our packages are named and set for independent versioning (remember?) with . lerna The final step to this stage of the project is to add scripts for linting at the root package.json. These scripts can be combined into pre-commit hooks, pre-build hook to get unified formatting. Will leave that up to you. : , "lint" "eslint './packages/**/*.{ts,tsx}'" : "lint:fix" "eslint './packages/**/*.{ts,tsx}' --fix" Final for me looks something like . Hope, it’s the same for you. package.json this { : , : , : [ ], : { : , : , : }, : { : }, : { : , : , : , : , : , : , : , : , : , : , : , : , : , : } } "name" "root" "private" true "workspaces" "packages/*" "scripts" "lint" "eslint './packages/**/*.{ts,tsx}'" "lint:fix" "eslint './packages/**/*.{ts,tsx}' --fix" "build" "yarn workspace ui-components tsc && yarn workspace ui-core tsc" "devDependencies" "lerna" "^3.18.4" "dependencies" "@types/react" "^16.9.11" "@types/react-dom" "^16.9.4" "@typescript-eslint/eslint-plugin" "^2.7.0" "@typescript-eslint/parser" "^2.7.0" "eslint" "^6.6.0" "eslint-config-prettier" "^6.6.0" "eslint-config-react" "^1.1.7" "eslint-plugin-prettier" "^3.1.1" "eslint-plugin-react" "^7.16.0" "eslint-plugin-react-hooks" "^2.3.0" "prettier" "^1.19.1" "react" "^16.12.0" "react-dom" "^16.12.0" "typescript" "^3.7.2" Did you run yet? Did you get to test the commands and see what the linter/prettier can report back to you what your naked eyes miss while focusing on logic? Here’s what it reported to me yarn run lint Almost all prettier (formatting) error for now. Shall we fix it with another command? I did it with To verify I ran again to check if this actually. works. Voila! my errors are gone. yarn run lint:fix. yarn run lint ✌️ 🎉🍻 Our project supports which completes this section Congratulations! common linting, one command build process for each package . Sigh! My project is linting properly. Trust me this is all tested, iterated and so simplified for you to read. It wasn’t the same for me the first time. I remember pulling hair quite a few times and it took days. Wish I had an end-to-end guide for all this modern recommended setup of tools. Isn’t this long and detailed? I like pinning down every important detail just to avoid half baked information and ever-growing comment threads reporting back the issues. Although It’s possible if you’re using different versions of tools/packages, some things might change. Despite this, I’m sure I was able to carry this article in a way which binds reasoning to the steps carried out and their importance. This is not complete. No way! Remember, this guide is about startups 👨💻 🏢guide to building modern design-system. We have just started. I’m going to continue this post in another article. Until then show your ❤️ and feedback. Here’s the link to the completed that I have released recently. Turtlemint UI