Getting started
Introduction
Lerna is a fast, modern build system for managing JavaScript/TypeScript monorepos. Now maintained by Nx (v5+), it focuses on task running and publishing workflows.
Installation
# Install Lerna
npm install --global lerna
# Or use with npx
npx lerna@latest init
# Initialize new monorepo
mkdir my-monorepo && cd my-monorepo
npx lerna@latest init
Quick Start
# Run command in all packages
lerna run build
# Run tests in parallel
lerna run test --parallel
# Publish packages
lerna publish
# Version packages
lerna version
Modern Lerna (v5+)
Lerna now delegates dependency management to package managers (npm/yarn/pnpm workspaces).
Removed in v9:
lerna bootstrap(usenpm install)lerna add(use package manager)lerna link(workspaces handle this)
Core Commands
Task Execution
# Run script in all packages
lerna run <script>
# Run in parallel (faster)
lerna run test --parallel
# Run with streaming output
lerna run build --stream
# Skip packages without script
lerna run test --skip-nx-cache
Versioning
# Interactive version bump
lerna version
# Specific bump type
lerna version patch
lerna version minor
lerna version major
# Prerelease versions
lerna version prerelease --preid beta
# Custom version
lerna version 1.2.3
# Graduate prerelease to stable
lerna version --conventional-graduate
Publishing
# Publish changed packages
lerna publish
# Publish from specific tag
lerna publish from-git
# Publish specific version
lerna publish from-package
# Publish with dist-tag
lerna publish --dist-tag next
# Skip npm (only git tags)
lerna publish --no-push
List Packages
# List all packages
lerna list
# Show package paths
lerna list --long
# Show as JSON
lerna list --json
# Show dependency graph
lerna list --graph
# List changed packages
lerna changed
Filtering & Scoping
Scope Filters
# Run in specific package
lerna run build --scope=package-1
# Multiple packages
lerna run test --scope=package-1 --scope=package-2
# Pattern matching
lerna run build --scope='@myorg/*'
# Exclude packages
lerna run test --ignore=package-1
Dependency Filters
# Run in packages that depend on changed
lerna run build --include-dependencies
# Since specific commit
lerna run test --since=main
# Since last tag
lerna run build --since
# Include dependents
lerna run build --include-dependents
Location Filters
# Packages in specific directory
lerna run build --scope='packages/core/*'
# Exclude by path
lerna run test --ignore='packages/experimental/*'
Configuration
lerna.json
{
"$schema": "node_modules/lerna/schemas/lerna-schema.json",
"version": "independent",
"npmClient": "pnpm",
"command": {
"publish": {
"conventionalCommits": true,
"message": "chore(release): publish"
},
"version": {
"allowBranch": ["main", "next"]
}
},
"packages": ["packages/*"]
}
Version Modes
{
"version": "1.0.0"
}
Fixed mode - all packages share version
{
"version": "independent"
}
Independent mode - each package has own version
Nx Integration
{
"useNx": true,
"useWorkspaces": true,
"npmClient": "npm"
}
nx.json Configuration
Task Pipeline
{
"tasksRunnerOptions": {
"default": {
"runner": "nx/tasks-runners/default",
"options": {
"cacheableOperations": ["build", "test", "lint"]
}
}
},
"targetDefaults": {
"build": {
"dependsOn": ["^build"],
"outputs": ["{projectRoot}/dist"]
}
}
}
Caching
{
"tasksRunnerOptions": {
"default": {
"options": {
"cacheableOperations": ["build", "test"],
"cacheDirectory": ".nx/cache"
}
}
}
}
Parallel Execution
{
"parallel": 3,
"maxParallel": 5
}
Package Manager Integration
NPM Workspaces
// package.json
{
"name": "my-monorepo",
"private": true,
"workspaces": ["packages/*"]
}
// lerna.json
{
"npmClient": "npm",
"useWorkspaces": true
}
Yarn Workspaces
// package.json
{
"private": true,
"workspaces": {
"packages": ["packages/*"],
"nohoist": ["**/react-native"]
}
}
// lerna.json
{
"npmClient": "yarn",
"useWorkspaces": true
}
pnpm Workspaces
# pnpm-workspace.yaml
packages:
- "packages/*"
- "apps/*"
// lerna.json
{
"npmClient": "pnpm",
"useWorkspaces": true
}
Versioning Strategies
Conventional Commits
# Enable conventional commits
lerna version --conventional-commits
# Prerelease with conventional
lerna version --conventional-prerelease
# Graduate to stable
lerna version --conventional-graduate
Version Options
| Option | Description |
|---|---|
--exact |
No caret/tilde ranges |
--force-publish |
Force all packages |
--create-release github |
Create GitHub release |
--allow-branch main |
Only version from main |
--no-git-tag-version |
Skip git tag |
--no-push |
Don't push to git |
--yes |
Skip confirmation prompts |
--amend |
Amend previous commit |
--conventional-graduate |
Graduate prerelease to stable |
Changelog Generation
# Generate changelogs
lerna version --conventional-commits
# Custom changelog preset
lerna version --changelog-preset angular
// lerna.json
{
"command": {
"version": {
"conventionalCommits": true,
"changelogPreset": "conventionalcommits"
}
}
}
Publishing Workflow
Publish Options
| Option | Description |
|---|---|
--dist-tag next |
Publish with npm dist-tag |
--registry |
Custom npm registry |
--no-verify-access |
Skip access verification |
--contents dist |
Subdirectory to publish |
--otp |
One-time password for 2FA |
--legacy-auth |
Legacy auth token |
--pre-dist-tag |
Dist-tag for prereleases |
--temp-tag |
Temporary tag during publish |
--yes |
Skip confirmation |
--no-git-reset |
Leave changes after failed push |
--force-publish |
Force publish all packages |
--ignore-scripts |
Don't run lifecycle scripts |
--ignore-prepublish |
Don't run deprecated prepublish |
Publish Workflow
# 1. Version packages
lerna version --conventional-commits
# 2. Build packages
lerna run build
# 3. Publish to npm
lerna publish from-git
# Combined: version + publish
lerna publish --conventional-commits
Canary Releases
# Publish canary version
lerna publish --canary
# Canary from specific commit
lerna publish --canary --preid beta
# Canary with custom version
lerna publish --canary minor
Advanced Filtering
Complex Filters
# Changed since branch
lerna run test --since origin/main
# Include all dependencies
lerna run build \
--scope=my-app \
--include-dependencies
# Exclude private packages
lerna run build --no-private
# Only private packages
lerna run test --no-sort --private
Environment Variables
# Set concurrency
LERNA_CONCURRENCY=2 lerna run build
# Skip Nx cache
NX_SKIP_NX_CACHE=true lerna run test
# Verbose output
LERNA_VERBOSE=true lerna run build
Filtering by Changes
# Since last release
lerna run test --since
# Since specific ref
lerna run build --since=v1.0.0
# Excluding merge commits
lerna changed --since=HEAD^
# Force all packages
lerna run build --force-publish
Caching & Performance
Task Caching
# Skip cache for specific run
lerna run build --skip-nx-cache
# Clear cache
npx nx reset
# View cache status
lerna run test --verbose
Parallel Execution
# Run in parallel (max CPUs)
lerna run build --parallel
# Limit concurrency
lerna run test --concurrency=2
# Stream output
lerna run build --stream
# No sort (faster startup)
lerna run test --no-sort
Nx Cloud Integration
{
"tasksRunnerOptions": {
"default": {
"runner": "@nrwl/nx-cloud",
"options": {
"accessToken": "YOUR_TOKEN",
"cacheableOperations": ["build", "test", "lint"]
}
}
}
}
Common Workflows
Initial Setup
# Initialize monorepo
npx lerna@latest init
# Install dependencies
npm install
# Create first package
mkdir -p packages/core
cd packages/core
npm init -y
# Configure lerna.json
cat > lerna.json << 'EOF'
{
"version": "independent",
"npmClient": "npm",
"useWorkspaces": true,
"command": {
"publish": {
"conventionalCommits": true
}
}
}
EOF
# Configure workspaces
# Edit root package.json: "workspaces": ["packages/*"]
Development Workflow
# Install all dependencies
npm install
# Build all packages
lerna run build
# Watch mode for changed packages
lerna run dev --parallel
# Run tests
lerna run test --since origin/main
# Lint code
lerna run lint --stream
Release Workflow
# 1. Ensure clean working tree
git status
# 2. Pull latest changes
git pull origin main
# 3. Run tests
lerna run test
# 4. Version packages
lerna version --conventional-commits
# 5. Build for production
lerna run build
# 6. Publish to npm
lerna publish from-git
# 7. Push tags and commits
git push --follow-tags
CI/CD Pipeline
# .github/workflows/release.yml
name: Release
on:
push:
branches: [main]
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- uses: actions/setup-node@v3
with:
node-version: 18
registry-url: https://registry.npmjs.org
- run: npm ci
- run: lerna run build
- run: lerna run test
- name: Version and Publish
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
git config user.name "GitHub Actions"
git config user.email "actions@github.com"
lerna publish --yes --conventional-commits
Troubleshooting
Common Issues
Bootstrap deprecated
# ❌ Don't use
lerna bootstrap
# ✅ Use package manager
npm install
# or
yarn install
# or
pnpm install
Add package deprecated
# ❌ Don't use
lerna add lodash --scope=package-1
# ✅ Use package manager
cd packages/package-1
npm install lodash
Link deprecated
# ❌ Don't use
lerna link
# ✅ Workspaces handle linking
npm install
Debug Mode
# Verbose logging
lerna run build --loglevel verbose
# Debug Nx integration
lerna run test --verbose
# Check package resolution
lerna list --long --all
# Verify configuration
lerna info
Cache Issues
# Clear Nx cache
npx nx reset
# Force rebuild without cache
lerna run build --skip-nx-cache
# View cache directory
ls -la .nx/cache
Migration Guide
From Lerna v6 to v9
Removed commands:
lerna bootstrap→ Usenpm installlerna add→ Use package manager directlylerna link→ Automatic with workspaces
Configuration changes:
// Old lerna.json (v6)
{
"packages": ["packages/*"],
"npmClient": "npm",
"useWorkspaces": true,
"command": {
"bootstrap": {
"hoist": true
}
}
}
// New lerna.json (v9)
{
"version": "independent",
"npmClient": "npm",
"useWorkspaces": true,
"useNx": true
}
From Yarn Workspaces
# 1. Install Lerna
npm install --save-dev lerna
# 2. Initialize Lerna
npx lerna init
# 3. Update lerna.json
cat > lerna.json << 'EOF'
{
"npmClient": "yarn",
"useWorkspaces": true,
"version": "independent"
}
EOF
# 4. Keep existing package.json workspaces
# No changes needed to package.json
# 5. Start using Lerna commands
lerna run build
lerna version
lerna publish
Configuration Reference
lerna.json Schema
{
"$schema": "node_modules/lerna/schemas/lerna-schema.json",
"version": "independent",
"npmClient": "npm",
"useWorkspaces": true,
"useNx": true,
"packages": ["packages/*", "apps/*"],
"command": {
"publish": {
"conventionalCommits": true,
"message": "chore(release): publish %s",
"registry": "https://registry.npmjs.org",
"allowBranch": ["main", "next"],
"ignoreChanges": ["**/*.md", "**/*.test.js"]
},
"version": {
"allowBranch": ["main"],
"conventionalCommits": true,
"changelogPreset": "angular",
"createRelease": "github",
"message": "chore(release): %s"
},
"run": {
"stream": true
}
},
"ignoreChanges": ["**/__fixtures__/**", "**/__tests__/**", "**/*.md"]
}
Command Options
| Command | Description |
|---|---|
run |
Run npm script in packages |
exec |
Run shell command in packages |
version |
Bump version of packages |
publish |
Publish packages to npm |
changed |
List packages changed |
diff |
Show diff since last release |
list |
List all packages |
init |
Initialize Lerna monorepo |
info |
Print debugging information |
clean |
Remove node_modules from all |
import |
Import package with history |
Global Options
| Option | Description |
|---|---|
--loglevel |
Set log level |
--concurrency |
Parallel task limit |
--reject-cycles |
Fail on circular deps |
--no-sort |
Don't sort topologically |
--max-buffer |
Max buffer for child process |
--no-progress |
Disable progress bars |
--ci |
CI mode (no interactive) |
Gotchas
Package Hoisting
Lerna no longer manages hoisting. Use package manager features:
// package.json
{
"workspaces": {
"packages": ["packages/*"],
"nohoist": ["**/react-native", "**/react-native/**"]
}
}
Private Packages
Mark packages as private to prevent accidental publishing:
// packages/internal/package.json
{
"name": "@myorg/internal",
"version": "1.0.0",
"private": true
}
Version Conflicts
Fixed mode can cause issues with incompatible dependencies. Use independent mode:
{
"version": "independent"
}
Circular Dependencies
Lerna detects circular dependencies by default:
# Fail on cycles
lerna run build --reject-cycles
# Allow cycles (not recommended)
lerna run build --no-reject-cycles
Also see
- Lerna Official Docs - Official documentation
- Nx + Lerna Guide - Modern Lerna powered by Nx
- NPM Workspaces - Native npm workspace support
- Yarn Workspaces - Yarn workspace documentation
- pnpm Workspaces - pnpm workspace features
- Conventional Commits - Commit message convention
- Nx Documentation - Nx build system (powers modern Lerna)
- Monorepo Tools - Comparison of monorepo tools