Skip to main content
Software EngineeringTools & Productivity

Monorepos with Turborepo: Managing Multiple Packages

Set up a productive monorepo with Turborepo for shared UI libraries, APIs, and apps with smart caching and task orchestration.

Prince Mbeah Essilfie

Prince Mbeah Essilfie

April 22, 2026 at 06:04 AM

8 min read 2237 views
Monorepos with Turborepo: Managing Multiple Packages

Why Monorepos?

When you have a shared UI library, a web app, a mobile app, and an API — all using the same types and utilities — a monorepo keeps everything in sync.

Setup with Turborepo

npx create-turbo@latest my-monorepo

Structure:

my-monorepo/
├── apps/
│   ├── web/          ← Nuxt app
│   ├── api/          ← API server
│   └── docs/         ← Documentation site
├── packages/
│   ├── ui/           ← Shared component library
│   ├── config/       ← Shared ESLint, Tailwind configs
│   ├── database/     ← Prisma schema & client
│   └── types/        ← Shared TypeScript types
├── turbo.json
├── package.json
└── pnpm-workspace.yaml

Workspace Configuration

# pnpm-workspace.yaml
packages:
  - "apps/*"
  - "packages/*"
// turbo.json
{
  "tasks": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": [".output/**", "dist/**"]
    },
    "dev": {
      "cache": false,
      "persistent": true
    },
    "test": {
      "dependsOn": ["^build"]
    },
    "lint": {}
  }
}

Shared Packages

// packages/ui/package.json
{
  "name": "@myorg/ui",
  "exports": {
    ".": "./src/index.ts",
    "./Button": "./src/Button.vue"
  }
}

Consuming in an app:

// apps/web/package.json
{
  "dependencies": {
    "@myorg/ui": "workspace:*",
    "@myorg/types": "workspace:*"
  }
}
workspace:* tells pnpm to always use the local version. Changes to the shared package are immediately available in all consuming apps — no publishing needed.

Task Caching

Turborepo's killer feature: if inputs haven't changed, skip the task entirely:

$ turbo build

 Tasks:    4 successful, 4 total
 Cached:   3 cached, 4 total
 Time:     1.2s (3 cached)

Enable remote caching for team-wide sharing:

turbo login
turbo link

Now builds cached by one developer are reused by the entire team and CI.

Shared Configuration

// packages/config/tailwind.config.ts
import type { Config } from 'tailwindcss'

export default {
  theme: {
    extend: {
      colors: { brand: { 500: '#3b82f6' } },
    },
  },
} satisfies Config
// apps/web/tailwind.config.ts
import baseConfig from '@myorg/config/tailwind'

export default {
  presets: [baseConfig],
  content: ['./app/**/*.vue', '../../packages/ui/src/**/*.vue'],
}

When a Monorepo Makes Sense

✅ Multiple apps sharing code ✅ Atomic changes across packages (update types + UI + app in one PR) ✅ Team working across multiple packages

❌ Unrelated projects that happen to live together ❌ Teams that rarely collaborate ❌ When build times become unmanageable

Start with a monorepo if you foresee shared code. Splitting later is harder than starting unified.

TypeScriptDevOpsArchitecture
Prince Mbeah Essilfie

Written by Prince Mbeah Essilfie

Fullstack Software Developer

Read next

The Complete Guide to Vue 3 Composables
12 min read

The Complete Guide to Vue 3 Composables

Understand the power of Vue 3's Composition API through practical, real-world composable patterns that you can use today.

Prince Mbeah EssilfiePrince Mbeah Essilfie
1.1K