Back to Blog
EngineeringApr 22, 2026·8 min read·Ribbsaeter Systems Engineering

TypeScript Monorepos at Scale: Turborepo, pnpm and the Patterns That Actually Work

A field guide to structuring TypeScript monorepos for teams shipping multiple packages, apps and services — based on what we run in production across fintech, marketplace and SaaS clients.

Key takeaways

  • 01Turborepo + pnpm workspaces is the production-proven default for TypeScript monorepos in 2026.
  • 02Remote caching alone typically saves 40-60% of CI wall time — and it compounds as the repo grows.
  • 03Treat internal packages like published ones: proper exports maps, independent tsconfigs, explicit dependencies.
  • 04Use `--filter` flags in CI to build only what changed. Never rebuild the world on every commit.
  • 05Monorepos are not a silver bullet — they require strict boundary discipline or they degrade into a tangled mess faster than polyrepos do.

Every growing product team eventually faces the same fork: keep splitting code into more repositories, or consolidate into a monorepo. After shipping monorepos for fintech platforms, B2B marketplaces and SaaS products, we have a clear opinion: for TypeScript-heavy teams, a well-structured monorepo with Turborepo and pnpm is almost always the right call. But 'well-structured' is doing a lot of work in that sentence.

Why monorepos win for TypeScript teams

  • A single TypeScript version, a single ESLint config, a single Prettier config. Configuration drift across repos is eliminated by definition.
  • Shared packages — UI libraries, API clients, validation schemas — are imported directly. No publish-install-update cycle, no version matrix.
  • Atomic commits across apps and libraries. When you change a shared type, every consumer updates in the same PR.
  • One CI pipeline to maintain instead of N. Remote caching makes it fast.

Our default stack

We standardise on Turborepo for task orchestration and caching, pnpm workspaces for dependency management, and changesets for versioning when internal packages are also published. The repo structure follows a strict convention: apps/ for deployable applications (Next.js frontends, Node APIs, React Native apps), packages/ for shared libraries (ui, config, tsconfig, types), and tooling/ for generators, scripts and CI helpers.

├── apps/
│   ├── web/          # Next.js frontend
│   ├── api/          # Node.js API
│   └── mobile/       # React Native app
├── packages/
│   ├── ui/           # Shared component library
│   ├── config/       # ESLint, Prettier, tsconfig presets
│   ├── types/        # Shared TypeScript types
│   └── validation/   # Zod schemas shared across apps
├── turbo.json
├── pnpm-workspace.yaml
└── package.json

Package boundaries are everything

The single most common monorepo failure mode is treating the repo as one giant codebase where anything can import anything. That is not a monorepo — it is a big ball of mud with a fancy build system. Every internal package must have an explicit exports map in its package.json, its own tsconfig that extends a shared base, and zero circular dependencies. We enforce this with a combination of Turborepo's dependency graph and a custom ESLint rule that flags cross-boundary imports.

Remote caching: the compounding advantage

Turborepo's remote cache stores the output of every build, lint and test task by content hash. When a developer or CI runner encounters the same inputs, it downloads the cached output instead of recomputing. On a typical monorepo with 8-12 packages, we see cache hit rates of 70-85% in CI and 50-60% locally. That translates to CI runs completing in 2-4 minutes instead of 8-12. Over a year, for a team of ten engineers, this saves hundreds of hours.

TypeScript configuration strategy

We maintain a base tsconfig in packages/config that sets strict mode, module resolution, path aliases and target. Each app and package extends this base and overrides only what it needs (jsx settings for React apps, module type for Node APIs). This eliminates the configuration drift that plagues polyrepo setups where each repo gradually diverges in strictness, target and module settings.

When monorepos are the wrong choice

  • Teams with fundamentally different deployment cadences and zero shared code. If your Python ML pipeline and your React dashboard share nothing, separate repos are fine.
  • Very large organisations (500+ engineers) where build times and access control become political. At that scale, consider Nx or Bazel instead of Turborepo.
  • Projects where a single team owns a single deployable artifact. A monorepo of one app is just a repo.

How we ship this for clients

When we build a new product for a client — whether it is a fintech dashboard, a marketplace platform or a SaaS tool — the monorepo is set up in the first sprint. CI, linting, type-checking and deployment pipelines are configured before the first feature PR. This is part of what we mean by production-grade infrastructure from day one: the boring plumbing that lets a senior team move fast without breaking things.

Frequently asked questions

Direct answers to questions readers and AI assistants commonly ask about this topic.

What is the best monorepo tool for TypeScript in 2026?+

Turborepo with pnpm workspaces is the most widely adopted and production-proven setup for TypeScript monorepos. Nx is a strong alternative for larger teams that need more opinionated scaffolding. Bazel is reserved for very large (500+ engineer) organisations.

Does a monorepo slow down CI?+

Not with proper tooling. Turborepo's remote caching and filtered builds mean most PRs run faster than equivalent polyrepo setups because only affected packages are rebuilt. We typically see 40-60% CI time reduction after migrating to a cached monorepo.

Can a monorepo include both web and mobile apps?+

Yes. Our standard monorepo structure includes Next.js web apps, Node.js APIs and React Native mobile apps in the same repository. Shared packages like UI components, validation schemas and TypeScript types are consumed by all three.

Last updated: April 27, 2026 · Written by Ribbsaeter Systems Engineering · Full-Stack Development