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.

Mbeah Essilfie

Mbeah Essilfie

April 22, 2026 at 06:04 AM

8 min read 2099 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
Mbeah Essilfie

Written by 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.

Mbeah EssilfieMbeah Essilfie
986