Native monorepo management with npm workspaces and on-demand package execution with npx. Node.js v22.x, npm v10.x (as of 2026-02-10).

Core Setup: npm Workspaces

Initialize a monorepo and define workspace roots in the root package.json to enable npm’s native monorepo capabilities.

// root/package.json
{
  "name": "my-monorepo",
  "version": "1.0.0",
  "private": true, // Prevents accidental publishing of the root package
  "workspaces": [ // Defines directories containing workspace packages
    "packages/*", // Example: packages/ui-lib, packages/utils
    "apps/*"      // Example: apps/web, apps/admin
  ],
  "scripts": {
    "build": "npm run build --workspaces", // Runs 'build' script in all workspaces
    "test": "npm test --workspaces"        // Runs 'test' script in all workspaces
  },
  "devDependencies": {
    "typescript": "^5.3.3" // Common dev dependencies are often hoisted to the root
  }
}

Each workspace package also has its own package.json.

// packages/ui-lib/package.json
{
  "name": "@my-monorepo/ui-lib", // Scoped package name for internal usage
  "version": "1.0.0",
  "main": "dist/index.js", // Entry point after build
  "scripts": {
    "build": "tsc" // Example build script for TypeScript
  },
  "dependencies": {
    "react": "^18.2.0" // UI library specific dependencies
  }
}

Local Package Linking & Consumption

An application within the monorepo consumes a shared UI library from another workspace using the workspace: protocol.

// apps/web/package.json
{
  "name": "@my-monorepo/web",
  "version": "1.0.0",
  "private": true, // Often true for applications not meant for publishing
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build"
  },
  "dependencies": {
    "@my-monorepo/ui-lib": "workspace:*", // Links to the local ui-lib package
    "react": "^18.2.0",
    "react-dom": "^18.2.0"
  }
}

The application can then import components directly from the linked workspace package.

// apps/web/src/App.js
import React from 'react';
import { Button } from '@my-monorepo/ui-lib'; // Import directly from the workspace package

function App() {
  return (
    <div>
      <h1>My Web App</h1>
      <Button onClick={() => alert('Clicked!')}>Click Me</Button>
    </div>
  );
}

export default App;

Dependency Hoisting & Resolution

npm install at the monorepo root intelligently manages dependencies, hoisting common ones to the root node_modules to optimize disk space and installation time.

# Execute from the monorepo root directory
npm install
# npm analyzes all workspace package.json files.
# Dependencies with compatible versions (e.g., 'react') are hoisted
# to the root 'node_modules' to avoid duplication.
# Unique or conflicting versions remain in their respective workspace's 'node_modules'.
# 'workspace:*' dependencies are automatically symlinked by npm,
# pointing to the actual local package directory.

Shared UI Library Development

Developing a reusable React component library and making it available to other applications within the monorepo.

// packages/ui-lib/src/Button.js
import React from 'react';

const Button = ({ children, onClick }) => { // Simple functional component
  return (
    <button
      style={{ padding: '10px 20px', backgroundColor: '#007bff', color: 'white', border: 'none', borderRadius: '5px', cursor: 'pointer' }}
      onClick={onClick}
    >
      {children}
    </button>
  );
};

export default Button;
// packages/ui-lib/src/index.js
export { default as Button } from './Button'; // Export components for consumption

Gotchas & Best Practices: Build Order

Shared packages must be built before any dependent applications. This ensures that the consuming application finds the compiled output when building itself.

// root/package.json (extended scripts for build orchestration)
{
  "name": "my-monorepo",
  "version": "1.0.0",
  "private": true,
  "workspaces": [
    "packages/*",
    "apps/*"
  ],
  "scripts": {
    "build:ui-lib": "npm run build --workspace=@my-monorepo/ui-lib", // Build specific workspace
    "build:web": "npm run build --workspace=@my-monorepo/web",
    "build:all": "npm run build:ui-lib && npm run build:web", // Enforce correct build order
    "start:web": "npm start --workspace=@my-monorepo/web",
    "lint": "npm run lint --workspaces" // Run linting across all workspaces
  },
  "devDependencies": {
    "lerna": "^8.0.0", // Optional: Lerna can assist with versioning and publishing workflows
    "typescript": "^5.3.3"
  }
}
  • Versioning: Use workspace:* for local dependencies to automatically link to the current version within the monorepo. For external packages, use exact versions or caret/tilde ranges.
  • private: true: Set private: true in package.json for packages not intended for public npm registry.

Advanced Pattern: npx for Temporary Tools

npx (Node Package eXecutor) runs npm package binaries without explicit installation, ideal for one-off commands or specific tool versions.

# Execute a package binary without installing it globally or locally
npx create-react-app my-new-app --template typescript
# npx temporarily downloads 'create-react-app' and its dependencies,
# executes the binary, then removes them.

# Run a specific version of a tool
npx eslint@8.56.0 . --fix
# Ensures a particular ESLint version is used, regardless of local install.

# Execute a binary from a local 'node_modules/.bin' (similar to npm run)
# If 'eslint' is a devDependency, 'npm run eslint' is common.
# 'npx eslint' also works and will find the local binary first.
npx eslint .
# Prioritizes local installation, then temporary download if not found.

# Security note: Always verify packages before executing with npx,
# especially from unknown sources, as it runs arbitrary code.

Advanced Pattern: CI/CD with GitHub Actions

Configuring GitHub Actions for monorepos with workspaces requires specific caching and explicit build steps.

# .github/workflows/ci.yml
name: Monorepo CI

on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4 # Use the latest stable checkout action

      - name: Setup Node.js
        uses: actions/setup-node@v4 # Use Node.js v22.x for 2026
        with:
          node-version: '22'
          cache: 'npm' # Enable npm cache
          cache-dependency-path: '**/package-lock.json' # Cache based on lock files

      - name: Install dependencies
        run: npm install # Installs all dependencies across workspaces, leveraging hoisting

      - name: Build UI Library
        run: npm run build --workspace=@my-monorepo/ui-lib # Explicitly build shared library first

      - name: Build Web Application
        run: npm run build --workspace=@my-monorepo/web # Then build the application

      - name: Run tests
        run: npm test --workspaces # Run tests across all workspaces
  • actions/checkout@v4: Ensures the repository is available.
  • actions/setup-node@v4: Sets up Node.js. cache: 'npm' and cache-dependency-path are crucial for performance, caching node_modules based on package-lock.json changes.
  • npm install: Run once at the root to install all workspace dependencies.
  • Explicit Build Order: Separate steps for building shared libraries and applications ensure correct dependency resolution.
  • npm test --workspaces: Runs tests in all defined workspaces.

Quick Reference

  • npm Workspaces: Native monorepo solution (npm v7+).
  • private: true: In root package.json and unpublishable workspaces.
  • workspaces array: Defines paths to workspace packages in root package.json.
  • workspace:*: Dependency range for linking local packages.
  • Hoisting: Common dependencies move to root node_modules.
  • npm install: Run at root to install all workspace dependencies.
  • npm run <script> --workspace=<name>: Run script in a specific workspace.
  • npm run <script> --workspaces: Run script in all workspaces.
  • npx: Executes package binaries without persistent installation.
    • Use for: One-off commands, specific tool versions, local binary execution.
    • Performance: Can be slower due to download, but avoids global pollution.
    • Security: Verify packages, as npx executes arbitrary code.
  • CI/CD: Cache node_modules, ensure correct build order (shared libs first).
  • Common Error: “Cannot find module” if shared library not built before app build.

References

  1. npm Workspaces Official Docs
  2. npm-install Official Docs
  3. npx Official Docs
  4. GitHub Actions setup-node

This page is AI-assisted. References official documentation.