Claude Code for Bun Workspaces — Workflow Guide

Written by Michael Lip · Solo founder of Zovo · $400K+ on Upwork · 100% JSS Join 50+ builders · More at zovo.one

The Setup

You are managing a monorepo using Bun’s built-in workspace support. Unlike npm or pnpm workspaces that need separate tools for linking and building, Bun handles workspaces natively with zero-config linking and fast installs. Claude Code can scaffold workspace packages and manage cross-dependencies, but it defaults to npm/yarn workspace patterns that conflict with Bun’s approach.

What Claude Code Gets Wrong By Default

  1. Adds lerna, turbo, or nx for workspace management. Claude installs monorepo tools on top of Bun. Bun workspaces handle dependency linking natively — bun install at the root links all packages automatically.

  2. Uses npm link or yarn link for local packages. Claude runs manual linking commands. Bun resolves workspace packages through the "workspaces" field in root package.json without any link step.

  3. Creates .npmrc or .yarnrc files. Claude generates package manager config files for the wrong runtime. Bun reads bunfig.toml for configuration, not npm or yarn config files.

  4. Writes CommonJS in workspace packages. Claude defaults to module.exports. Bun workspaces work best with ESM — use "type": "module" in each package’s package.json and export syntax in source files.

The CLAUDE.md Configuration

# Bun Monorepo with Workspaces

## Architecture
- Runtime: Bun
- Monorepo: Bun native workspaces
- Structure: packages/* and apps/* directories
- Config: bunfig.toml for Bun settings, NOT .npmrc

## Workspace Rules
- Root package.json has "workspaces": ["packages/*", "apps/*"]
- Run bun install at root only — links all packages automatically
- Reference workspace packages by name: "my-lib": "workspace:*"
- No lerna, no turbo, no nx — Bun handles it natively
- Scripts run from root: bun run --filter my-app dev
- Each package has its own package.json with "type": "module"

## Conventions
- Shared packages in packages/ directory
- Applications in apps/ directory
- Package names prefixed: @myorg/package-name
- Each package exports via package.json "exports" field
- TypeScript: each package has own tsconfig extending root
- Test with bun test (built-in test runner, no Jest needed)
- Never use require() — ESM imports only

Workflow Example

You want to create a shared UI component package used by two apps. Prompt Claude Code:

“Create a new workspace package called @myorg/ui in packages/ui with a Button component. Add it as a dependency to apps/web and apps/docs. Make sure TypeScript types are exported correctly.”

Claude Code should create packages/ui/package.json with proper "exports" and "types" fields, write the Button component with TypeScript, add "@myorg/ui": "workspace:*" to both apps’ package.json files, and run bun install at the root to link everything.

Common Pitfalls

  1. Forgetting workspace:* protocol. Claude adds dependencies with version numbers like "@myorg/ui": "^1.0.0". Bun workspaces require the workspace:* protocol to resolve local packages instead of looking them up in the npm registry.

  2. Running bun install in individual packages. Claude runs install in each package directory separately. With Bun workspaces, always run bun install from the monorepo root — it handles all packages at once and creates the correct symlinks.

  3. TypeScript path mapping conflicts. Claude adds paths in tsconfig.json to resolve workspace packages. Bun’s workspace linking makes packages available by name already. Adding redundant path mappings causes resolution conflicts when both Bun and TypeScript try to resolve the same import.