Monorepo Setup Guide Builder

Document monorepo structure, tooling, and conventions for your team

Generate a monorepo guide covering directory structure, tooling config for Turborepo, Nx, Lerna or pnpm workspaces, shared package conventions, a versioning strategy, and a CI/CD pipeline outline. It runs free in your browser on Gera Tools, with nothing uploaded.

Last updated Source: Gera Tools

What is a monorepo?

A monorepo holds many projects — apps and shared libraries — in a single repository. It makes sharing code, coordinating changes across projects, and running atomic cross-project commits much easier than juggling many separate repos.

One repo, many projects, clear rules

A monorepo only stays pleasant if everyone follows the same structure and conventions. This builder generates the guide that makes that happen: a labeled directory tree, the config for your chosen tool, how shared packages are referenced, your versioning strategy, and a CI/CD outline. Commit it at the repo root and new contributors know where everything goes.

How it works

You name the organization and package scope (like @acme), then list your apps and shared packages. The tool renders a directory tree with apps/* for deployable applications and packages/* for internal libraries, and shows how an app depends on a package using the workspace protocol ("@acme/ui": "workspace:*") so apps always use the local source.

It then emits tool-specific configuration for your choice — a turbo.json task pipeline, an Nx project config, a Lerna setup note, or a pnpm-workspace.yaml. The versioning section explains the trade-off between fixed and independent releases, and the CI/CD outline describes the install-frozen-lockfile, lint, test, build, and deploy steps a pipeline should run.

Choosing between Turborepo, Nx, Lerna, and pnpm workspaces

Turborepo is the most popular choice for new projects. It focuses on task caching and pipeline orchestration: define which tasks depend on which in turbo.json, and Turborepo only re-runs tasks whose inputs have changed. Minimal config, fast results.

Nx is more opinionated and includes code generators, plugin ecosystem, and a dependency graph UI. It suits larger teams where scaffolding consistency matters. The learning curve is steeper but the capabilities go further — module federation, distributed task execution, enforced build boundaries.

Lerna predates both and is now maintained by Nrwl (same team as Nx). It handles versioning and publishing well (especially with Changesets) but does less for build caching. Common to see it paired with Nx.

pnpm workspaces with no orchestration layer is the simplest starting point. Just a pnpm-workspace.yaml linking packages together. Add caching tools (Turborepo can sit on top of pnpm workspaces) when the repo grows.

Sample Turborepo config generated by this builder

{
  "pipeline": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": [".next/**", "dist/**"]
    },
    "test": {
      "dependsOn": ["build"],
      "outputs": []
    },
    "lint": {
      "outputs": []
    },
    "dev": {
      "cache": false
    }
  }
}

The ^build means: before building this package, build all packages it depends on. Turborepo resolves the dependency graph and runs tasks in the right order and in parallel where safe.

Structuring apps and packages

The standard layout the builder generates:

my-repo/
├── apps/
│   ├── web/          ← deployable Next.js app
│   └── api/          ← deployable Express API
├── packages/
│   ├── ui/           ← shared component library
│   ├── config/       ← eslint, tsconfig presets
│   └── utils/        ← shared helpers
├── turbo.json
├── pnpm-workspace.yaml
└── package.json

Apps import packages using the workspace protocol in their package.json:

{ "dependencies": { "@acme/ui": "workspace:*" } }

This means apps always use the in-repo version during development — no publish step needed. In CI, pnpm install --frozen-lockfile with Turborepo caching means only changed apps and their dependants rebuild.

Tips for keeping monorepos maintainable

  • Keep the apps/packages split strict. If one “app” is imported by another app, it is really a shared package — move it to packages/.
  • Run affected-only tasks in CI so a one-line change does not trigger a full rebuild.
  • Pin Node and package manager versions in .nvmrc and engines in package.json so every machine and runner matches.
  • Use a single top-level tsconfig.json with references pointing to each package, rather than duplicating compiler settings.
  • Enforce module boundaries: apps should not import directly from other apps; all sharing goes through packages/.