Understanding Monorepos: A Practical Guide

Kishan KareliyaKishan Kareliya
5 min read

Introduction

Monorepos have become increasingly popular, especially for large-scale projects. In this blog, we will explore what monorepos are, when to use them, their benefits, and the challenges they bring. This guide will help you understand monorepos and get hands-on with setting up a simple one using TurboRepo, along with examples of a packages folder. By the end, you'll have a clear understanding of how monorepos can streamline your development process.

What is a Monorepo?

In simple terms, a monorepo is a way to manage multiple projects in a single Git repository. These projects can share code and dependencies, making it easier to maintain consistency across them. Unlike polyrepos (one project per repo), a monorepo allows for easier code sharing and management of dependencies between multiple projects.

Why Use a Monorepo?

Monorepos are most useful when:

  • You have multiple projects that share code (e.g., a UI library).

  • You need consistent tooling across projects.

  • You want to streamline development and deployment processes.

When Should You Avoid a Monorepo?

Monorepos aren’t necessary for every project. If you are working on a small project or a single application, you may not need one. Monorepos also require additional tooling to manage builds efficiently, which may not be necessary for small teams or projects.

Setting Up a Monorepo with TurboRepo

TurboRepo is a tool that optimizes the development process by enabling you to build, lint, and test only what has changed. This makes monorepos much faster to work with. Let's create a simple monorepo setup using TurboRepo.

Step 1: Install TurboRepo

Start by setting up a new project with TurboRepo. Install the package globally or as a dev dependency:

npm install turbo -g

Step 2: Project Structure

We'll create a structure where we have two applications (web and admin) and one shared package (ui-library).

/my-monorepo
  /apps
    /web
    /admin
  /packages
    /ui-library

Step 3: Setup turbo.json

The turbo.json file is the configuration file for TurboRepo. It helps specify what tasks to run for different parts of your monorepo.

{
  "pipeline": {
    "build": {
      "dependsOn": ["^build"]
    },
    "lint": {},
    "test": {}
  }
}

This turbo.json defines a build, lint, and test pipeline. The dependsOn field ensures that each build depends on the builds of its dependencies.

Step 4: Setup packages Folder

Inside the packages folder, we'll place shared code that can be reused by multiple apps. For example, we can create a UI component library.

Example: ui-library Package

  1. Create a simple UI component in packages/ui-library/src/Button.tsx.
import React from 'react';

const Button = () => {
  return <button>Click me!</button>;
};

export default Button;
  1. Add package.json for the ui-library package:
{
  "name": "ui-library",
  "version": "1.0.0",
  "main": "src/index.ts",
  "dependencies": {
    "react": "^17.0.0"
  }
}
  1. Create a tsconfig.json for TypeScript support:
{
  "compilerOptions": {
    "target": "es5",
    "module": "commonjs",
    "jsx": "react",
    "strict": true
  }
}

Step 5: Setup apps Folder

We'll now configure our apps to use this shared package from packages/ui-library.

Example: web App

  1. Create a simple React app in apps/web.

  2. Inside apps/web/src/App.tsx, import the shared Button component from the ui-library package:

import React from 'react';
import Button from 'ui-library/src/Button';

function App() {
  return (
    <div>
      <h1>Welcome to the Web App</h1>
      <Button />
    </div>
  );
}

export default App;
  1. Update the package.json of the web app to include the shared package:
{
  "name": "web",
  "version": "1.0.0",
  "dependencies": {
    "ui-library": "workspace:*"
  }
}

Example: admin App

  1. Create a similar React app in apps/admin.

  2. Use the same Button component from the ui-library package in the admin app.

Step 6: Running TurboRepo

Now, let’s build and run the entire monorepo using TurboRepo.

turbo run build

TurboRepo will automatically determine the build order based on the dependencies between your apps and packages. For example, if you make a change in ui-library, TurboRepo will only rebuild the affected apps (web and admin).

Advantages of Using a Monorepo

  • Code Sharing: You can share code (e.g., UI components) between multiple projects without duplicating it.

  • Consistent Tooling: By sharing tools like ESLint, Prettier, and TypeScript configurations across projects, you maintain a consistent code style and structure.

  • Simplified Dependency Management: Dependencies are installed only once at the root, saving time and space.

Challenges and Solutions

Maintaining Configurations

As your monorepo grows, managing different configurations (e.g., ESLint, TypeScript, Tailwind) across multiple projects can become complex. To solve this, create configuration packages in the packages folder.

Example:

{
  "name": "eslint-config-custom",
  "version": "1.0.0",
  "main": "index.js"
}

Use these configurations across all apps by referencing them.

Slow CI Pipelines

In large monorepos, building every app/package on every commit can slow down your CI pipeline. TurboRepo solves this by caching builds and only rebuilding the parts of your monorepo that have changed.

Conclusion

Monorepos, when managed effectively, can make your development process much smoother, especially for larger teams and projects. Using tools like TurboRepo helps optimize workflows by caching and streamlining builds. While setting up a monorepo comes with its challenges, the benefits of shared code, consistent tooling, and simplified dependency management often outweigh the difficulties. Feel free to explore this monorepo setup with TurboRepo for your projects, and see how it can improve your development workflow! For further reading, consider exploring additional resources and documentation on monorepos and TurboRepo.

0
Subscribe to my newsletter

Read articles from Kishan Kareliya directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Kishan Kareliya
Kishan Kareliya