Lerna is a fast, modern build system for managing and publishing multiple JavaScript/TypeScript packages from the same repository
Lerna is a monorepo management tool designed to streamline the development, building, and publishing of multiple related JavaScript or TypeScript packages within a single repository. It solves the coordination challenges that arise when maintaining interdependent packages—such as shared libraries, component systems, or modular application architectures—by automating versioning, handling local package linking, and orchestrating task execution across your workspace.
Originally created to solve Facebook's internal tooling challenges and later adopted by projects like Babel, Jest, and React Router, Lerna has evolved from a simple bootstrapping tool into a modern build system. Since version 5, it has incorporated advanced features like local and distributed caching, parallel task execution with dependency awareness, and Nx-powered task orchestration. This evolution positions it as both a package publisher and a performance-focused build tool.
The package is particularly valuable for teams publishing npm libraries from monorepos, as it handles the complex workflows of detecting changed packages, bumping versions in lockstep or independently, generating changelogs, creating git tags, and pushing to npm registries—all while respecting semantic versioning conventions. With over 1.6 million weekly downloads, Lerna has become a foundational tool in the JavaScript ecosystem for organizations managing component libraries, design systems, and plugin architectures.
Lerna works alongside—not against—native workspace features in npm, Yarn, and pnpm. It delegates dependency installation and linking to these package managers while adding higher-level orchestration for publishing, task running, and caching that workspace implementations don't provide out of the box.
// lerna.json - configure caching and workspaces
{
"version": "independent",
"npmClient": "pnpm",
"useWorkspaces": true,
"command": {
"publish": {
"conventionalCommits": true,
"message": "chore(release): publish"
}
},
"targetDefaults": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**"],
"cache": true
}
}
}
// package.json (root) - define workspace packages
{
"name": "my-monorepo",
"private": true,
"workspaces": ["packages/*"],
"scripts": {
"build": "lerna run build",
"test": "lerna run test --stream",
"publish": "lerna publish"
},
"devDependencies": {
"lerna": "^9.0.4"
}
}
// packages/utils/package.json
{
"name": "@myapp/utils",
"version": "1.2.0",
"scripts": {
"build": "tsc",
"test": "jest"
}
}
// packages/app/package.json
{
"name": "@myapp/web",
"version": "2.0.5",
"dependencies": {
"@myapp/utils": "^1.2.0"
},
"scripts": {
"build": "vite build",
"test": "vitest"
}
}
// Common workflow commands:
// Install dependencies and link local packages
// $ pnpm install
// Build all packages in topological order (utils before app)
// $ lerna run build
// → @myapp/utils:build (cache miss, builds)
// → @myapp/web:build (cache miss, builds)
// Second run hits cache, completes in <1s
// Run tests only in changed packages since last commit
// $ lerna run test --since HEAD~1 --stream
// Create a new package with scaffolding
// $ lerna create @myapp/analytics packages/analytics
// Publish changed packages with auto-versioning
// $ lerna publish --conventional-commits
// → detects @myapp/web changed, prompts for version bump
// → updates dependencies in other packages
// → creates git tags, pushes to npmPublishing a component library: A team maintains 30 React components as separate packages (e.g., @company/button, @company/modal) in one repo. Lerna detects which components changed since the last release, bumps their versions respecting semver, updates cross-dependencies automatically, and publishes only affected packages to npm with a single lerna publish command.
Running tests across packages in dependency order: In a monorepo with API clients, shared utilities, and application packages, running lerna run test executes test scripts respecting the dependency graph—utilities test first, then clients that depend on them, then apps. Unchanged packages are skipped via caching, reducing CI time from 15 minutes to 3 minutes on incremental builds.
Managing independent versioning for plugins: A CLI tool with 50 plugins uses Lerna's independent mode ("version": "independent" in lerna.json) so each plugin can have its own version number. Plugin A goes from 1.2.0 to 1.3.0 while Plugin B stays at 2.5.1, and Lerna handles the complexity of updating cross-references in package.json files.
Bootstrapping a new monorepo package: A developer runs lerna create @myapp/analytics packages/analytics to scaffold a new package with standard structure, then lerna bootstrap to symlink it to other local packages that import it, enabling immediate local development without publishing to npm.
Distributed task execution for large teams: An enterprise monorepo with 200 packages uses Lerna with Nx Cloud for distributed caching. When a developer rebuilds after pulling changes, Lerna fetches pre-built artifacts from remote cache instead of rebuilding 180 unchanged packages, reducing build time from 45 minutes to 2 minutes.
npm install lernapnpm add lernabun add lerna