C.W.K.
Stream
Lesson 02 of 05 · published

Project References: Monorepo TS

~8 min · tooling, project-references, monorepo

Level 0Curious
0 XP0/93 lessons0/23 achievements
0/100 XP to next level100 XP to go0% complete
"Project references partition a monorepo so the compiler can avoid re-checking what hasn't changed."

The problem

In a monorepo with multiple TypeScript packages (e.g., packages/api, packages/web, packages/shared), a naive tsc rechecks everything every time. For large monorepos this can take minutes — way too long for a development loop.

Project references

Each package gets its own tsconfig.json. The root tsconfig.json lists all the packages as references. The packages also reference each other via the same field where they depend.

tsc -b (build mode) reads the references, computes the dependency graph, and compiles in topological order. The result is cached in .tsbuildinfo files. On subsequent runs, only packages whose inputs changed are recompiled — and only their direct dependents need to be re-checked.

Each package's tsconfig needs composite: true

Project-reference packages must set "composite": true in their tsconfig. This enables incremental builds, requires explicit outDir, and forces declaration emission (so dependents can read the types). The flag is the opt-in.

Project references are the canonical way to scale TS in a monorepo. Without them, every check rebuilds everything; with them, only the affected packages.

Code

Project reference setup — root and packages·json
// Root tsconfig.json — orchestrator.
{
  "files": [],
  "references": [
    { "path": "./packages/shared" },
    { "path": "./packages/api" },
    { "path": "./packages/web" }
  ]
}

// packages/shared/tsconfig.json — composite project.
{
  "compilerOptions": {
    "composite": true,
    "declaration": true,
    "outDir": "./dist",
    "rootDir": "./src"
  },
  "include": ["src/**/*"]
}

// packages/api/tsconfig.json — references shared.
{
  "compilerOptions": {
    "composite": true,
    "outDir": "./dist",
    "rootDir": "./src"
  },
  "include": ["src/**/*"],
  "references": [{ "path": "../shared" }]
}

External links

Exercise

Set up a tiny two-package monorepo: pkg-a exports a function, pkg-b imports it. Configure project references. Run tsc -b. Change pkg-a, run again — confirm only pkg-a and pkg-b rebuild, not the whole world.
Hint
After the first build, .tsbuildinfo files appear in each package. The second build reads them, sees only pkg-a's inputs changed, and only recompiles pkg-a + its dependents.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.