Applications 6 min

What Is a Monorepo?

Short answer

A monorepo is a single version-controlled repository that contains multiple, often related, projects or packages, such as a frontend app, a backend API, and shared libraries. Instead of each project living in its own git repository, everything sits in one codebase with one commit history. This makes it easier to share code, coordinate changes across projects, and keep dependencies in sync, at the cost of needing better tooling as the repository grows.

A monorepo is a single repository that holds the code for multiple applications or packages, versioned and managed together instead of split across separate repositories.

Companies like Google, Meta, and Microsoft run their entire engineering organization out of one enormous repository. Smaller teams increasingly borrow the same idea at a much smaller scale, not because it is fashionable, but because it solves a concrete problem: keeping related projects in sync without a maze of version bumps and cross-repository pull requests.

The problem a monorepo solves

Imagine a product with a public website, a customer dashboard, and a shared design system package. In separate repositories, a change to a shared button component means publishing a new version of the design system package, then updating the version number in both the website and the dashboard, then opening pull requests in three places for what is conceptually one change. Reviewers see three disconnected diffs instead of one coherent story.

A monorepo collapses this into a single commit: the design system change and both consuming applications update together, reviewed as one unit, tested against each other before merging. There is no window where the website is running an outdated version of a component that was already fixed elsewhere.

What actually lives inside a monorepo

A typical monorepo is organized into workspaces or packages, each with its own package.json (or equivalent manifest) but sharing one root configuration, one .git history, and usually one CI pipeline. A common layout looks like:

/apps
  /website
  /dashboard
/packages
  /ui-components
  /shared-config
  /api-client

Applications in /apps import directly from packages in /packages using local workspace references rather than published npm versions. When you edit ui-components, every app using it picks up the change immediately in local development, no publish step required.

Shared code without publishing overhead

The biggest practical win is skipping the publish-and-bump cycle for internal code. In a multi-repo setup, an internal library still has to be versioned and released like any external dependency, even though only your own team consumes it. In a monorepo, internal packages are just folders; there is no version number to bump and no registry to push to before other projects can use the update.

This matters most for design systems, shared TypeScript types between frontend and backend, and utility libraries that several apps rely on. It removes an entire category of “works locally but the deployed version is stale” bugs.

Coordinated versioning and atomic changes

Because everything shares one commit history, a single pull request can touch a shared library and every app that depends on it, and CI can validate the whole set together before merge. This is what people mean by an atomic change: the fix and its consequences land at the same time, rather than being split across a library release and several follow-up dependency bumps in different repositories.

It also simplifies large-scale refactors. Renaming a shared function used by five applications is one search-and-replace and one pull request in a monorepo, versus five coordinated releases in separate repositories.

Tooling you need as it grows

The trade-off is that git and your build system were not originally designed to treat a folder of unrelated projects as one unit efficiently. Once a monorepo grows past a few dozen packages, plain npm install or a full test run across everything becomes noticeably slower. Tools like Turborepo and Nx address this by caching build and test results per package and only re-running what actually changed since the last build, based on the dependency graph.

For a small team maintaining two or three related projects, workspace support built into pnpm or npm is usually enough; you do not need Nx or Bazel on day one. Add dedicated tooling when build times or CI queue times start to hurt, not before.

Our tip: Start a monorepo with plain package manager workspaces (pnpm or npm) and only add a build orchestrator like Turborepo once you notice CI runs testing packages that did not change. Adding tooling before you feel the pain adds configuration overhead for no benefit yet.

Frequently asked questions

Is a monorepo the same as a monolith?

No, these describe different things. A monolith is about deployment architecture: one application deployed as a single unit. A monorepo is about source code organization: multiple projects stored in one repository. You can have a monorepo containing several independently deployed microservices, and you can have a monolithic application split across multiple repositories. The two concepts are often confused but solve different problems.

What are the main downsides of a monorepo?

As a monorepo grows, plain git operations like clone, checkout, and status can slow down because the tool has to process the entire history and file tree, not just one project. Build times can also increase unless you introduce incremental builds and caching. Without clear ownership boundaries (via code owners or workspace configuration), it becomes easier for teams to accidentally depend on each other's internals. Large organizations solve this with dedicated tooling such as Nx, Turborepo, or Bazel, but for a small team the added tooling can be overhead you do not yet need.

Does a small business or startup need a monorepo?

If you maintain a handful of tightly related projects, for example a website, a marketing site, and a shared component library, a monorepo can save real time by letting you update shared code once and see it reflected everywhere immediately. If your projects are unrelated or owned by fully separate teams with different release cycles, separate repositories are usually simpler to reason about. The decision should follow how much code and how many releases the projects actually share, not team size alone.

What tools are commonly used to manage a monorepo?

For JavaScript and TypeScript projects, npm, pnpm, and Yarn all support workspaces, which let multiple packages share one lockfile and reference each other locally. On top of workspaces, tools like Turborepo and Nx add task caching, dependency graph awareness, and the ability to only rebuild or retest what actually changed. For polyglot codebases at large scale, Google's Bazel and Meta's Buck handle builds across many languages in a single repository.

Want a dashboard or automation built for you?

Stuurboard BI builds custom Power BI dashboards and workflow automations. Pragmatic, fast, and without the fuss.