OneApp Docs
Core Concepts

Dependency Management

Master pnpm's powerful dependency management to add, update, and maintain packages across 40+ workspaces efficiently.

Why proper dependency management matters

Managing dependencies incorrectly in a monorepo causes serious problems:

  • Wrong location — Adding packages to root instead of specific workspaces breaks isolation
  • Version conflicts — Different packages use incompatible versions of the same dependency
  • Phantom dependencies — Code relies on unlisted dependencies, breaking CI
  • Bloated installs — Unnecessary dependencies slow down installation
  • Security vulnerabilities — Outdated packages expose your apps to attacks

OneApp uses pnpm with workspace-specific dependency management — installing packages exactly where needed, preventing phantom dependencies with strict mode, and using content-addressable storage — ensuring fast, reliable, and secure dependency management.

Use cases

Master dependency management to:

  • Add packages efficiently — Install to the right workspace on first try
  • Update safely — Review and test updates before merging
  • Debug quickly — Find why a package is installed and resolve conflicts
  • Maintain security — Keep dependencies up-to-date and vulnerability-free
  • Optimize installs — Leverage pnpm's caching for 3x faster installations

How it works

Dependencies are managed at three levels:

Structure
{
  "root": {
    "devDependencies": "Workspace-wide tools (ESLint, TypeScript, Prettier)"
  },
  "packages": {
    "dependencies": "Package-specific runtime dependencies",
    "devDependencies": "Package-specific dev tools",
    "peerDependencies": "Dependencies provided by consumers"
  },
  "apps": {
    "dependencies": "App-specific dependencies + workspace packages"
  }
}

Adding dependencies

To a specific workspace

# Production dependency
pnpm --filter=web add lodash

# Development dependency
pnpm --filter=web add -D vitest

# Peer dependency (for libraries)
pnpm --filter=@repo/ui add -P react react-dom

When to use:

  • Production dependencies: Required at runtime (React, Next.js, database clients)
  • Dev dependencies: Only needed during development (TypeScript, testing tools)
  • Peer dependencies: Consumer must provide (React for UI libraries)

To the root workspace

# Add dev dependency to root (workspace-wide tooling only)
pnpm add -D -w prettier eslint typescript

Root dependencies

Only add to root for tools used across ALL packages:

  • ✅ Formatters (Prettier)
  • ✅ Linters (ESLint)
  • ✅ TypeScript compiler
  • ❌ Package-specific dependencies (these go in the package)

Internal dependencies

Internal packages automatically use workspace:*:

# Add internal package
pnpm --filter=web add @repo/ui

# Results in package.json:
# "@repo/ui": "workspace:*"

Benefits:

  • ✅ Always links to local version during development
  • ✅ No version mismatches
  • ✅ Changes reflect instantly
  • ✅ Converts to real version when publishing

Updating dependencies

Update specific package

# Update to latest compatible version
pnpm --filter=web update lodash

# Update to specific version
pnpm --filter=web update lodash@4.17.21

# Update to latest (ignore semver)
pnpm --filter=web update lodash@latest

Update all dependencies

# Update all in a workspace
pnpm --filter=web update

# Update all across monorepo
pnpm -r update

# Interactive update (choose which to update)
pnpm --filter=web update -i

Check for outdated packages

# Check outdated in specific workspace
pnpm --filter=web outdated

# Check outdated across all workspaces
pnpm -r outdated

Removing dependencies

# Remove from workspace
pnpm --filter=web remove lodash

# Remove from root
pnpm remove -w prettier

# Remove dev dependency
pnpm --filter=web remove -D vitest

Understanding workspace:*

What it means

The workspace:* protocol tells pnpm to:

  1. Development: Link to local package via symlink
  2. Type checking: Use local TypeScript types
  3. Publishing: Convert to actual version (if applicable)

Example workflow

apps/web/package.json
{
  "dependencies": {
    "@repo/ui": "workspace:*"
  }
}

What pnpm does:

# During development
node_modules/@repo/ui symlink to ../../packages/ui

# Your changes in packages/ui/ are immediately available in apps/web/

Troubleshooting

Dependency not found

# Clear cache and reinstall
pnpm store prune
pnpm install

# Force reinstall
pnpm install --force

Version conflicts

# Check why a version is installed
pnpm why lodash

# Shows all packages depending on lodash

Solution:

package.json (root)
{
  "pnpm": {
    "overrides": {
      "lodash": "4.17.21" // Force this version everywhere
    }
  }
}

Clean reinstall

# Nuclear option - full clean
pnpm clean && pnpm install

# Or manually
rm -rf node_modules
rm -rf **/node_modules
rm pnpm-lock.yaml
pnpm install

Best practices

1. Pin exact versions for apps

apps/web/package.json (production app)
{
  "dependencies": {
    "next": "16.0.0", // ✅ Exact version
    "react": "19.0.0" // ✅ Exact version
  }
}

Why: Ensures reproducible builds in production

2. Use ranges for peer dependencies

packages/ui/package.json (library)
{
  "peerDependencies": {
    "react": "^19.0.0" // ✅ Compatible versions
  }
}

Why: Allows consumers to use compatible versions

3. Keep root deps minimal

package.json (root) - ONLY workspace-wide tools
{
  "devDependencies": {
    "prettier": "^3.0.0",
    "typescript": "^5.0.0",
    "turbo": "^2.0.0"
  }
}

4. Test after updates

# Full test suite
pnpm build && pnpm lint && pnpm typecheck

# Package-specific
pnpm --filter=web build
pnpm --filter=web test

Next steps

On this page