Skip to content
Innovialabs

Monorepo for Web Projects: Turborepo, Nx and pnpm Workspaces

Advanced4 min

In short

A monorepo keeps the website, admin, API and shared packages in one repository; pnpm workspaces link the packages, while Turborepo or Nx speed up builds with caching and running only affected tasks.

What a monorepo gives you

A monorepo is a single Git repository holding several apps and libraries. A typical web project: a public site, an admin panel, an API and shared packages — UI components, types, configs.

The main benefits:

  • Shared types between frontend and API. Change a field in a model and TypeScript immediately shows every place that broke.
  • One design system for the site and admin without publishing packages to npm.
  • Atomic changes: the API and client fix land in one commit and one pull request.
  • Uniform rules: one set of linters, formatting and dependency versions.

The cost is tooling: without it, building and testing every package on every change gets slow.

A sample layout

my-project/
├── apps/
│   ├── web/          # public site (Next.js)
│   ├── admin/        # admin panel
│   └── api/          # backend (Node.js)
├── packages/
│   ├── ui/           # shared components
│   ├── types/        # shared types and schemas
│   └── config/       # eslint, tsconfig
├── package.json
├── pnpm-workspace.yaml
└── turbo.json

The rule: apps depend on packages, never on each other. If the admin needs code from the site, move it into packages/.

Layer 1: pnpm workspaces

pnpm workspaces link packages inside the repository. You only declare where they live:

# pnpm-workspace.yaml
packages:
  - "apps/*"
  - "packages/*"

Then add a local package to an app:

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

pnpm links the local folder, so changes in packages/ui show up in apps/web right away. On its own, pnpm does not know what to rebuild or cache — that is the orchestrator's job.

Layer 2: task orchestration and caching

An orchestrator understands the dependency graph between packages and can:

  • run tasks (build, test, lint) in the right order and in parallel;
  • cache results: if a package's inputs have not changed, the result comes from cache instead of being recomputed;
  • run tasks only for packages affected by a change;
  • share the cache between developers and CI via a remote cache.

A Turborepo configuration example:

{
  "$schema": "https://turborepo.com/schema.json",
  "tasks": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": ["dist/**", ".next/**", "!.next/cache/**"]
    },
    "lint": {},
    "test": { "dependsOn": ["^build"] }
  }
}

^build means "build this package's dependencies first". The config format changes between major versions, so check the docs for the version you use.

Turborepo, Nx or just pnpm

Criterionpnpm workspacesTurborepoNx
What it isPackage managerLightweight task runnerFull monorepo platform
Task cachingNoYes, local and remoteYes, local and remote
Learning curveMinimalLowHigher
Code generators, pluginsNoMinimalMany
Enforcing package boundariesNoLimitedDependency rules built in
Best for2–3 packages, simple scriptsMost JS/TS projectsLarge repos, many teams

A practical path: start with pnpm workspaces, add Turborepo when builds get noticeably slow, and consider Nx if you need strict boundaries, generators and large scale.

Common mistakes

  • Circular dependencies between packages: ui imports from types and types imports from ui.
  • Importing from a sibling app instead of moving code into a shared package.
  • Wrong outputs in cache settings: the cache restores the wrong files or nothing at all.
  • Environment variables missing from the cache key: a build with different values reuses a stale result.
  • Different versions of the same library across apps for no reason.
  • Secrets in shared packages: code from packages/ can end up in the client bundle.

FAQ

No. It pays off when there are several apps with shared code: a site and admin, web and API, several sites on one design system.

We can help with this

If you'd rather get the result than figure it all out yourself — this is what we do every day.

Discuss a project

Read next

Have an idea or a task? Tell us about it

We'll reply within one business day and propose a solution. The consultation is free.