Depend on packages by name

A workspace:* dependency is the edge turbo orders and hashes by.

From turborepo, best-practices/packages.md.

Reach-in import vs dependency

Everything is cached. Edit ui and build.

Relative importBad
apps/web/app/page.tsximport { Button } from
  "../../packages/ui/src/button"
web sees ui v10 stale hits
apps/web deps: { "next": "…" }
Declared dependencyGood
apps/web/app/page.tsximport { Button } from
  "@repo/ui/button"
web sees ui v10 stale hits
"@repo/ui": "workspace:*"
// packages/ui/package.json
{
  "name": "@repo/ui",
  "exports": { "./button": "./src/button.tsx" }   // JIT: the app's bundler compiles it
}

// apps/web/package.json (npm/yarn use "*")
{ "dependencies": { "@repo/ui": "workspace:*" } }

// apps/web/app/page.tsx
import { Button } from "@repo/ui/button";

Why it matters, pros and cons

Why it matters

Turbo builds its graph from package.json dependencies. An import it can't see adds no edge, so no order and no invalidation.

Pros

  • ^build orders the build.
  • A ui edit busts web's cache.
  • --affected and turbo prune follow it.

Cons

  • A JIT package has no build of its own to cache.
  • A compiled package needs dist/** in outputs.
  • No prebuild that cds into other packages.