{"post":{"slug":"how-to-set-up-a-monorepo-2026","title":"How to set up a monorepo in 2026","excerpt":"Monorepos let teams share code and coordinate releases without publishing packages. This guide covers tooling, workspace setup, and CI caching strategies for 2026.","category":"code","date":"March 16, 2026","author":"ByteLedger Team","content":"<span class=\"dropcap\">A</span> monorepo keeps multiple packages or applications in one git repository. The appeal is sharing code, coordinating versioning, and keeping cross-cutting changes in a single PR. The trap is turning it into a slow, undifferentiated blob. In 2026, the tooling is good enough that the setup is straightforward — the hard part is keeping package boundaries clean.\n\n## What changed in 2026\n\n- **Turborepo 3** added native support for `--affected` flag, running tasks only for packages touched since the base branch — the same feature Nx had as its main differentiator.\n- **pnpm 10** improved workspace protocol resolution and hoisting behavior; `pnpm-lock.yaml` is now fully deterministic across platforms.\n- **Nx 20** introduced project crystal, inferring project graph from file structure without an `nx.json` configuration file for most frameworks.\n- **Bun workspaces** are production-stable; Bun monorepos are ~3× faster for install and script execution — worth evaluating for new TypeScript-only repos.\n\n## Directory structure\n\n```\nmy-monorepo/\n├── apps/\n│   ├── web/          # Next.js app\n│   └── api/          # Fastify server\n├── packages/\n│   ├── ui/           # Shared React components\n│   ├── config/       # Shared ESLint, TS, Prettier configs\n│   └── utils/        # Shared utilities\n├── pnpm-workspace.yaml\n├── turbo.json\n├── package.json\n└── tsconfig.base.json\n```\n\n## Step 1 — pnpm workspaces\n\n```yaml\n# pnpm-workspace.yaml\npackages:\n  - 'apps/*'\n  - 'packages/*'\n```\n\nRoot `package.json`:\n\n```json\n{\n  \"private\": true,\n  \"scripts\": {\n    \"build\": \"turbo build\",\n    \"dev\": \"turbo dev --parallel\",\n    \"lint\": \"turbo lint\",\n    \"test\": \"turbo test\"\n  },\n  \"devDependencies\": {\n    \"turbo\": \"^3.0.0\"\n  }\n}\n```\n\nInstall from the root:\n\n```bash\npnpm install\n```\n\n## Step 2 — Turborepo pipeline\n\n```json\n// turbo.json\n{\n  \"$schema\": \"https://turbo.build/schema.json\",\n  \"tasks\": {\n    \"build\": {\n      \"dependsOn\": [\"^build\"],\n      \"outputs\": [\"dist/**\", \".next/**\", \"!.next/cache/**\"]\n    },\n    \"test\": {\n      \"dependsOn\": [\"^build\"],\n      \"outputs\": []\n    },\n    \"lint\": {\n      \"outputs\": []\n    },\n    \"dev\": {\n      \"cache\": false,\n      \"persistent\": true\n    }\n  }\n}\n```\n\n`\"^build\"` means: build all dependencies before building this package.\n\n## Step 3 — shared TypeScript config\n\n```json\n// tsconfig.base.json\n{\n  \"compilerOptions\": {\n    \"strict\": true,\n    \"target\": \"ES2022\",\n    \"moduleResolution\": \"bundler\",\n    \"paths\": {}\n  }\n}\n```\n\nEach package extends it:\n\n```json\n// packages/utils/tsconfig.json\n{\n  \"extends\": \"../../tsconfig.base.json\",\n  \"compilerOptions\": {\n    \"outDir\": \"dist\",\n    \"rootDir\": \"src\"\n  },\n  \"include\": [\"src\"]\n}\n```\n\n## Remote caching\n\nRemote caching is the highest-leverage Turborepo feature. It shares build artifacts across all developers and CI machines:\n\n```bash\n# Authenticate with Vercel Remote Cache (free)\nnpx turbo login\nnpx turbo link\n```\n\nAdd to CI:\n\n```yaml\nenv:\n  TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}\n  TURBO_TEAM: ${{ vars.TURBO_TEAM }}\n```\n\nWith remote caching, a CI run that builds only changed packages drops from 5 min to ~30 s on warm cache.\n\n## Tooling comparison\n\n| Tool | Strengths | Weaknesses |\n| --- | --- | --- |\n| Turborepo | Simple setup, Vercel remote cache free | Less powerful affected graph than Nx |\n| Nx | Affected commands, generators, plugins | Steeper config, heavier |\n| Lerna | Historical — avoid in 2026 | Unmaintained diff; superseded |\n| Bun workspaces | Fastest install + run | Smaller ecosystem, less Node compatibility |\n\n## How to pick\n\n1. **Small to mid team, mostly TypeScript?** → pnpm + Turborepo. 30 minutes to set up.\n2. **Large org, multiple languages, complex dependency graph?** → Nx.\n3. **Greenfield TypeScript-only, performance is priority?** → Bun workspaces.\n4. **Publishing packages to npm?** Add Changesets for versioning: `pnpm add -D -w @changesets/cli`.\n\n## Common mistakes\n\n**Putting everything in one package.** A monorepo where `packages/shared` contains 80 % of the code is a monolith with extra steps. Keep packages small and single-purpose.\n\n**Not setting `\"private\": true` on workspace packages.** Without it, `pnpm publish` can accidentally publish internal packages.\n\n**Circular dependencies.** Package A importing from package B which imports from A breaks Turborepo's task ordering. Use the `madge` tool to detect cycles.\n\n**Ignoring cache invalidation.** If `turbo.json` outputs do not match the actual build output directories, caching silently does nothing.\n\n## What to skip\n\n- **Lerna.** It is effectively legacy. pnpm workspaces handles package management; Turborepo handles task running.\n- **Yarn workspaces for new projects.** pnpm is strictly better: faster installs, correct dependency isolation, no phantom dependencies.\n- **Custom task runner scripts.** Before writing `scripts/build-all.sh`, check if `turbo build --filter=...` already does what you need.\n\n## FAQ\n\n**Can I mix JavaScript and TypeScript packages?**\nYes. Configure each package independently. TypeScript packages compile to `dist/`; JS packages can export from `src/` directly.\n\n**How do I add a new package?**\nCreate the directory under `apps/` or `packages/`, add a `package.json` with a unique `name`, and run `pnpm install` from the root.\n\n**Should apps and packages be in separate directories?**\nConvention says yes: `apps/` for runnable applications (web, API, CLI), `packages/` for libraries. This makes intent clear and simplifies tooling rules.\n\n**How do I run a command in only one package?**\n`turbo build --filter=web` runs only the `web` app and its dependencies. Or `pnpm --filter=web build` for a single package without Turbo.\n\n## Where to go next\n\n- [How to publish an npm package in 2026](/blog/how-to-publish-an-npm-package-2026)\n- [How to write a GitHub Action in 2026](/blog/how-to-write-a-github-action-2026)\n- [How to set up pre-commit hooks in 2026](/blog/how-to-set-up-pre-commit-hooks-2026)","hue":309,"metaTitle":"How to Set Up a Monorepo in 2026: pnpm, Turborepo, Nx","metaDescription":"Guide to setting up a monorepo in 2026 with pnpm workspaces and Turborepo. Covers workspace config, task pipelines, remote caching, and CI integration.","keywords":["how to set up a monorepo 2026","pnpm workspaces monorepo","turborepo setup 2026","nx monorepo 2026","monorepo best practices 2026","turborepo remote cache","javascript monorepo tooling","monorepo ci caching","pnpm turborepo guide","monorepo package structure 2026"],"tldr":["**pnpm workspaces + Turborepo is the 2026 default** — pnpm handles installs efficiently, Turbo handles task orchestration and caching.","**Remote caching is the biggest win** — Turborepo remote cache means CI never rebuilds an artifact that has not changed.","**Nx is the alternative for large orgs** — better dependency graph visualization and affected-command targeting, at the cost of steeper setup.","**Keep package boundaries real** — a monorepo is not an excuse to have one giant `src/` folder; each package should be independently importable.","**Skip:** using Lerna in 2026 — it has been superseded; use pnpm workspaces for package management and Turborepo for task running."],"readTime":"4 min read","wordCount":838},"related":[{"slug":"nx-vs-turborepo-2026","title":"Nx vs Turborepo 2026: Which Monorepo Tool Wins","excerpt":"Nx and Turborepo both cache tasks to speed up monorepo builds, but they solve different problems. This guide compares caching, generators, and boundaries so you can pick the right tool in 2026.","category":"code","date":"August 7, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":308},{"slug":"turborepo-monorepo-setup-2026","title":"Turborepo monorepo setup in 2026: a real apps + packages walkthrough","excerpt":"Build a Turborepo monorepo from scratch in 2026 — apps, shared packages, caching, CI integration, and the gotchas most \"Hello World\" guides miss.","category":"code","date":"February 7, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":142},{"slug":"monorepo-tooling-compared-2026","title":"Monorepo Tooling Compared: What You Actually Need","excerpt":"Monorepo tools solve task orchestration and caching, not repository layout. Here is what each tier provides and how to tell when you have outgrown the simple option.","category":"code","date":"August 19, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":150}],"sameCategory":[{"slug":"bazel-build-explained-2026","title":"Bazel Explained: What Hermetic Builds Actually Cost","excerpt":"Bazel delivers reproducible, precisely cached builds and demands that you describe everything. Here is what you get, what it costs, and who should say no.","category":"code","date":"August 19, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":145},{"slug":"graphql-federation-2026","title":"GraphQL Federation: One Graph, Many Teams","excerpt":"Federation composes independently-owned subgraphs into a single API. Here is what it solves, the operational cost it adds, and when a simpler gateway is enough.","category":"code","date":"August 19, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":300},{"slug":"hermetic-builds-explained-2026","title":"Hermetic Builds Explained: Same Inputs, Same Output","excerpt":"A hermetic build depends only on declared inputs. Here is what that property gives you, the common leaks that break it, and how to move toward it incrementally.","category":"code","date":"August 19, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":130},{"slug":"monorepo-tooling-compared-2026","title":"Monorepo Tooling Compared: What You Actually Need","excerpt":"Monorepo tools solve task orchestration and caching, not repository layout. Here is what each tier provides and how to tell when you have outgrown the simple option.","category":"code","date":"August 19, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":150}],"newer":{"slug":"how-to-set-goals-2026","title":"How to set goals in 2026: a system that gets results, not just intentions","excerpt":"Setting a goal without a system behind it is just a wish. This is the 2026 framework for writing goals that stick, tracking them simply, and actually finishing.","category":"guides","date":"March 16, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":140},"older":{"slug":"how-to-set-up-a-second-screen-2026","title":"How to set up a second screen in 2026: every connection type explained","excerpt":"A second monitor is the fastest productivity upgrade for most desk workers. This 2026 guide covers HDMI, USB-C, DisplayPort, wireless, and laptop lids.","category":"tech","date":"March 16, 2026","author":"ByteLedger Team","readTime":"4 min read","hue":242}}