Ova

How to Install Dependencies Using Lerna?

Published in Lerna Dependency Management 6 mins read

Installing dependencies in a Lerna monorepo involves both setting up existing package dependencies and adding new ones to specific packages. While Lerna historically provided its own commands like lerna bootstrap, modern Lerna workflows often leverage native package manager workspaces (like npm or Yarn workspaces) for a streamlined experience.

Understanding Dependency Management in Lerna

Lerna is a tool that optimizes the management of multi-package repositories (monorepos) with Git and npm. It helps in hoisting common dependencies to the root of the monorepo, linking local packages, and ensuring consistent versioning. When working with Lerna, you're primarily dealing with two scenarios for dependency installation:

  1. Initial or General Installation: Setting up all existing dependencies across your monorepo.
  2. Adding New Dependencies: Installing a new dependency to one or more specific packages within your monorepo.

General Installation for Existing Dependencies

For a fresh clone of your Lerna monorepo or after changes in package.json files, the primary way to install all existing dependencies across all packages is by running your package manager's install command at the monorepo root.

  • Using npm (recommended with modern Lerna):

    npm install

    This command, when executed in the root of a monorepo configured with workspaces in its package.json, will install all dependencies for the root and all defined packages. This is the most common and efficient method for initial setup or after pulling changes.

  • Using lerna bootstrap (older Lerna versions or specific needs):

    lerna bootstrap

    Prior to Lerna v7, lerna bootstrap was the go-to command. It handles npm install (or Yarn/pnpm equivalent) for each package, links local packages, and hoists common dependencies. While still available, leveraging native workspaces with npm install is generally preferred for its simplicity and direct integration with your chosen package manager.

Adding New Dependencies to Specific Packages

When you need to introduce a new dependency to just one or a few specific packages within your Lerna monorepo, you have powerful options using either Lerna's add command or the native npm install -w (npm workspaces) command.

1. Using lerna add <dependency> --scope <package>

Lerna provides a dedicated command to add a new dependency to a targeted package or set of packages within your monorepo. This command is particularly useful for maintaining control over dependency scope.

  • Syntax:

    lerna add <dependency-name>[@version] --scope=<package-name-or-glob> [--dev] [--exact] [--peer]
  • How it works:

    1. It installs the specified dependency.
    2. It adds the dependency to the package.json of the targeted package(s).
    3. It then runs a general install (like npm install) at the root to ensure all dependencies are correctly linked and hoisted.
  • Example: To add lodash as a production dependency to a package named my-ui-component:

    lerna add lodash --scope=my-ui-component

    This command will add lodash to the dependencies section of packages/my-ui-component/package.json.

  • Example for devDependencies: To add jest as a development dependency to an internal utility package:

    lerna add jest --scope=my-utils --dev

2. Using npm install <dependency> -w <package> (npm Workspaces)

For monorepos utilizing npm workspaces (which is common with modern Lerna setups), you can directly use npm's --workspace (or -w) flag to install dependencies into specific packages. This method is often preferred for its directness and alignment with standard npm practices.

  • Syntax for a single package:

    npm install <dependency-name>[@version] -w <package-name-or-path> [--save-dev] [--save-exact]
  • How it works:

    1. The -w flag tells npm to perform the installation within the specified workspace (package).
    2. The dependency is added to the package.json of that workspace.
    3. Npm then handles the installation, respecting hoisting rules configured for the monorepo.
  • Example: To add axios as a production dependency to a package located at packages/api-client:

    npm install axios -w packages/api-client

    This command will add axios to the dependencies section of packages/api-client/package.json.

  • Example for devDependencies: To add typescript as a development dependency to a shared-types package:

    npm install typescript -w packages/shared-types --save-dev

3. Using npm install <dependency> -w <package1> -w <package2> (Multiple Packages)

The npm install -w command can be used multiple times to add the same dependency to several specific packages simultaneously.

  • Syntax for multiple packages:

    npm install <dependency-name>[@version] -w <package1-name-or-path> -w <package2-name-or-path> [...] [--save-dev]
  • Example: To add react-query to both packages/web-app and packages/admin-dashboard:

    npm install react-query -w packages/web-app -w packages/admin-dashboard

    This will add react-query to the dependencies section of both packages/web-app/package.json and packages/admin-dashboard/package.json.

Summary of Dependency Installation Methods

Method Description Use Case Example
npm install (at monorepo root) Installs all existing dependencies for the entire monorepo. Initial setup, after pulling changes, general dependency updates. npm install
lerna add <dep> --scope=<pkg> Adds a new dependency to a specific package(s) using Lerna's dedicated command. Targeted dependency addition, especially when not using native workspaces. lerna add react --scope=my-app
npm install <dep> -w <pkg> Adds a new dependency to a specific package using npm workspaces. Modern, targeted dependency addition for npm workspace users. npm install styled-components -w packages/design-system
npm install <dep> -w <pkg1> -w <pkg2> Adds a new dependency to multiple specific packages using npm workspaces. Adding the same dependency to several packages efficiently. npm install firebase -w packages/auth -w packages/data-service

Practical Insights and Best Practices

  • Leverage Workspaces: For modern Lerna setups, configuring workspaces in your root package.json is highly recommended. This allows you to use your package manager's native commands (npm install, npm add, npm rm) with the --workspace flag, simplifying dependency management.
    // root package.json
    {
      "name": "my-lerna-monorepo",
      "version": "1.0.0",
      "private": true,
      "workspaces": [
        "packages/*"
      ],
      "scripts": {
        "start": "lerna run start",
        "test": "lerna run test"
      },
      "devDependencies": {
        "lerna": "^7.x.x"
      }
    }
  • Root devDependencies for Tooling: Place common development tools (like ESLint, Prettier, Jest, TypeScript) in the root package.json's devDependencies. This allows all packages to access them without duplicating installations.
  • Internal Package References: Lerna (via workspaces) automatically handles linking your internal packages, so you can import { MyComponent } from 'my-ui-component' as if it were an external dependency, even if my-ui-component is another package within your monorepo.
  • Version Consistency: Strive for consistent versions of shared dependencies across your monorepo to avoid conflicts and simplify maintenance. Tools like Renovate Bot or Dependabot can help automate dependency updates.
  • Understanding Hoisting: When a dependency is used by multiple packages and they all request the same version, Lerna and workspaces will often "hoist" that dependency to the monorepo root node_modules to save disk space and installation time.

By understanding these methods and best practices, you can effectively manage dependencies in your Lerna monorepo, ensuring a smooth development workflow.