Skip to main content

Library vs CLI Packages: Local and Global Installation Guide and Usage Patterns

Overview

When working with Node.js and npm, you'll encounter two primary types of packages: library packages and CLI (Command-Line Interface) packages. Understanding the distinction between these package types is essential for effective development and proper dependency management.

What are Library Packages?

Library packages are modules that you import and use directly within your application code. They provide reusable functions, classes, and utilities that enhance your application's functionality.

Common examples include:

  • lodash - Utility functions for working with arrays, objects, and more
  • axios - HTTP client for making API requests
  • express - Web application framework
  • react - UI library for building user interfaces

Library packages are typically installed locally as project dependencies and imported into your code:

import express from "express";
import axios from "axios";

What are CLI Packages?

CLI packages are tools that run directly from your terminal or command line. They provide executable commands that perform specific tasks, often related to development workflows, project scaffolding, or build processes.

Common examples include:

  • vite - Frontend build tool and development server
  • create-react-app - React project generator
  • eslint - Code linting tool
  • typescript - TypeScript compiler

CLI packages are distinguished by their executable files, which are referenced in the package's package.json under the bin field. When installed, npm creates symbolic links or wrapper scripts in node_modules/.bin/ (for local installations) or in a system-wide bin directory (for global installations). These executables typically begin with a shebang line (#!/usr/bin/env node) that instructs the operating system to execute the file using Node.js.

How CLI Packages Work

Example of a CLI package configuration: CLI packages include executable files that allow them to run as terminal commands. Understanding how these executables are created and invoked is essential for working with CLI tools effectively.

Anatomy of a CLI Package

Unlike library packages that export modules for import, CLI packages define executable entry points through their package.json configuration:

{
"name": "my-cli-tool",
"version": "1.0.0",
"bin": {
"my-tool": "./bin/cli.js"
}
}

The bin field maps command names to executable file paths. In this example, the package name is my-cli-tool, but the actual command that users run is my-tool (defined in the bin field). This allows package authors to create user-friendly command names that differ from the npm package name.

The corresponding executable file (bin/cli.js) includes a shebang directive and the command logic:

#!/usr/bin/env node

console.log("Hello from my CLI tool!");
// Your CLI logic here

Key components:

  • Shebang line (#!/usr/bin/env node): Tells the system to execute this file using Node.js
  • bin field: Maps command name(s) to executable file path(s)
  • Executable permissions: The file must be executable (npm handles this during installation)

The Executable File

When you install a CLI package, npm automatically sets up these executables so they can be invoked from the command line. This cross-platform mechanism allows JavaScript-based tools to function as native commands without requiring separate shell scripts for different operating systems.

CLI Packages in Action

If you've worked with React, you've likely used Vite to scaffold a new project:

npm create vite@latest

This command uses Vite's CLI to interactively set up a new React (or Vue, Svelte, etc.) project with the necessary configuration and file structure.

Key Differences

AspectLibrary PackagesCLI Packages
UsageImported in application codeExecuted from terminal
PurposeProvide functionality to your appPerform development tasks
InstallationUsually local (npm install)Can be local or global
AccessVia import or requireVia command line

Packages That Are Both Library and CLI

Many packages serve dual purposes, functioning both as importable libraries and command-line tools. These hybrid packages offer maximum flexibility in how you integrate them into your workflow.

Installation Note: When installing CLI packages locally, they're typically added as development dependencies since they're used during development rather than in production:

npm install -D typescript eslint prettier
# or
npm install --save-dev typescript eslint prettier

Common examples:

TypeScript

  • As CLI: tsc app.ts - Compile TypeScript files
  • As Library: Import TypeScript compiler API in your build tools
// As library
import * as ts from "typescript";
const result = ts.transpile('let x: string = "hello"');

// As CLI
// tsc --init
// tsc src/app.ts

ESLint

  • As CLI: eslint src/ - Lint your code from terminal
  • As Library: Integrate ESLint into build processes or editors
// As library
import { ESLint } from "eslint";
const eslint = new ESLint();
const results = await eslint.lintFiles(["src/**/*.js"]);

// As CLI
// eslint . --fix
// eslint src/ --ext .js,.ts

Prettier

  • As CLI: prettier --write . - Format code from command line
  • As Library: Integrate formatting into your applications
// As library
import prettier from "prettier";
const formatted = prettier.format("const x={a:1}", { parser: "babel" });

// As CLI
// prettier --write src/
// prettier --check .

Webpack

  • As CLI: webpack - Bundle your application
  • As Library: Use webpack API in custom build scripts
// As library
import webpack from "webpack";
import config from "./webpack.config.js";

webpack(config, (err, stats) => {
// Handle build results
});

// As CLI
// webpack --mode production
// webpack serve --mode development

Why this matters:

  • Flexibility: Use the same tool in different contexts
  • Integration: Embed functionality directly into your applications
  • Automation: Programmatically control tools in build scripts
  • Consistency: Same underlying logic whether used as library or CLI

Local vs Global Installation

Understanding where to install packages is just as important as knowing what type they are.

Installing packages locally means they're added to your project's node_modules folder and listed in package.json.

npm install <package-name>

Where local packages are stored:

Library packages are stored in node_modules/<package-name>/. For example:

  • Installing lodash creates node_modules/lodash/
  • Installing axios creates node_modules/axios/
  • Installing react creates node_modules/react/

Each package folder contains:

  • package.json - Package metadata and dependencies
  • index.js or main entry file - The code you import
  • node_modules/ - The package's own dependencies (nested)

CLI packages have their executable files stored in node_modules/.bin/. For example:

  • Installing vite locally creates node_modules/.bin/vite
  • Installing eslint locally creates node_modules/.bin/eslint

Running locally installed packages:

# Library packages - import in your code
import axios from 'axios'; // Node.js finds it in node_modules/axios/

# CLI packages - multiple ways to run
npx vite # Using npx (recommended)
./node_modules/.bin/vite # Direct path reference
npm run dev # Via npm scripts where "dev": "vite"

Benefits:

  • Different projects can use different versions
  • Dependencies are tracked in package.json
  • Team members get consistent versions
  • No version conflicts between projects
  • CLI tools are isolated per project

Global Installation

Global packages are installed system-wide and available from any directory.

npm install -g <package-name>

When to use global installation:

  • CLI tools you use across multiple projects
  • Development utilities not tied to specific projects
  • Tools you want available system-wide

Note: Modern best practices often favor using npx to run CLI tools without global installation, ensuring you always use the correct version for each project.

Where global packages are stored:

Global packages are installed in a system-wide directory, separate from your project folders. The exact location varies by operating system and Node.js installation method:

  • Windows: C:\Users\<YourUsername>\AppData\Roaming\npm\node_modules
  • macOS/Linux: /usr/local/lib/node_modules or ~/.npm-global/node_modules

To find your global package installation directory, use:

npm root -g

This command displays the exact path where npm installs global packages on your system.

Example:

npm install -g typescript
# TypeScript is now available globally
tsc --version

# Check where it was installed
npm root -g
# Output: /usr/local/lib/node_modules (or your system's global path)

Global CLI executables are placed in a directory that's added to your system's PATH, making them accessible from any terminal location. Common locations include:

  • Windows: C:\Users\<YourUsername>\AppData\Roaming\npm
  • macOS/Linux: /usr/local/bin

Best Practices

  1. Install libraries locally - Always add application dependencies to your project
  2. Use npx for CLI tools - Run CLI packages without global installation: npx vite
  3. Document in package.json - Ensure all dependencies are tracked
  4. Avoid global installs when possible - They can cause version conflicts between projects

Understanding these concepts will help you manage dependencies more effectively and maintain cleaner, more reproducible development environments.