NVM vs NPM vs Node.js: What's the Difference? Complete Guide
When starting out with JavaScript development, you will inevitably encounter three terms: NVM, NPM, and Node.js. In short, Node.js is the JavaScript runtime, NPM is the package manager that ships with Node.js, and NVM is a version manager that lets you install and switch between multiple Node.js versions on the same machine. This article breaks down NVM vs NPM vs Node.js in detail, including the correct installation flow, essential commands, and best practices for team collaboration.
NVM vs NPM vs Node.js â Quick Overview
Before diving into each tool individually, here is a side-by-side comparison table that clarifies the role of NVM, NPM, and Node.js:
| Aspect | Node.js | NPM | NVM |
|---|---|---|---|
| What it is | JavaScript runtime environment | Package manager | Node.js version manager |
| Primary purpose | Execute JavaScript on the server | Install, manage, and publish packages | Install and switch between Node.js versions |
| Installation | Download from nodejs.org or install via NVM | Auto-installed with Node.js | Install via curl script or Homebrew |
| Key commands | node, node -v | npm install, npm run | nvm install, nvm use |
| Config files | None (uses env variables) | package.json, .npmrc | .nvmrc |
| Install location | /usr/local/bin/node or ~/.nvm/versions/ | Ships with Node.js; global packages in npm root | ~/.nvm/ |
A simple analogy:
- Node.js is the engine (lets JavaScript run outside the browser)
- NPM is the fuel station (provides packages/libraries)
- NVM is the garage (stores and manages multiple engine versions)
What Is Node.js?
Node.js is an open-source, cross-platform runtime environment built on Google Chrome’s V8 JavaScript engine. It enables JavaScript to run outside the browser â on servers, desktops, and IoT devices.
The V8 Engine
V8 is Google’s high-performance JavaScript engine, originally built to speed up JavaScript execution in Chrome. Its core innovation is JIT (Just-In-Time) compilation, which compiles JavaScript directly to machine code at runtime rather than interpreting it line by line. Node.js extracts V8 from the browser, allowing JavaScript code to execute natively on any operating system.
Why Use Node.js for Backend Development?
Before Node.js, backend development required languages like PHP, Java, Python, or Ruby. Frontend engineers who wanted to build server-side logic had to learn an entirely different language. Node.js lets developers use a single language â JavaScript â for both client and server code, dramatically lowering the barrier to full-stack development.
Node.js also introduces non-blocking I/O and an event-driven architecture. Traditional multi-threaded servers block a thread while waiting for database queries or file reads. Node.js uses a single-threaded event loop that continues processing other requests during I/O waits, making it ideal for high-concurrency applications like real-time chat, streaming services, and API gateways.
LTS vs Current Release Strategy
Node.js maintains two release channels:
| Channel | Description | Recommended for |
|---|---|---|
| LTS (Long Term Support) | Stable, with extended bug-fix support (18 months Active + 12 months Maintenance) | Production, enterprise projects |
| Current | Latest features, new major version every 6 months, less battle-tested | Development, experimenting with new APIs |
Even-numbered major versions (18, 20, 22) become LTS releases. Odd-numbered versions (19, 21) receive short-term maintenance only. Always use LTS for production workloads.
Common Node.js Use Cases
- API servers â Build RESTful or GraphQL APIs with Express.js, Fastify, or Hono.
- CLI tools â Tools like ESLint, Webpack, and Vite are all built on Node.js.
- SSR (Server-Side Rendering) â Frameworks like Next.js and Nuxt render pages on the server for better SEO.
- Real-time applications â WebSocket-based chat, notifications, and collaborative editing.
- Microservices â Node.js’s lightweight footprint is ideal for containerized microservice architectures.
# Check current Node.js version
node -v
# Start the REPL (interactive mode)
node
# Run a JavaScript file
node index.js
# List Node.js built-in modules
node -e "console.log(require('module').builtinModules)"
What Is NPM (Node Package Manager)?
NPM (Node Package Manager) is the default package manager for Node.js and is installed automatically alongside it. NPM provides a command-line interface for installing, updating, and managing third-party packages, as well as a public registry â the world’s largest open-source software repository with over 2 million packages as of 2024.
Essential npm Commands
| Command | Description |
|---|---|
npm init | Initialize a new project and create package.json |
npm init -y | Initialize with default values (skip prompts) |
npm install | Install all dependencies from package.json |
npm install <pkg> | Install a package and add to dependencies |
npm install <pkg> --save-dev | Install and add to devDependencies |
npm install -g <pkg> | Install a package globally |
npm uninstall <pkg> | Remove a package |
npm update | Update all packages to the latest allowed version |
npm run <script> | Run a script defined in package.json |
npm list | List installed packages |
npm outdated | Show packages with newer versions available |
package.json and package-lock.json
package.json is the project manifest. It records the project name, version, scripts, and dependency version ranges.
package-lock.json is an auto-generated lock file that pins the exact resolved version of every package (including transitive dependencies). It guarantees that different machines and CI/CD environments reproduce the identical dependency tree.
{
"name": "my-project",
"version": "1.0.0",
"scripts": {
"start": "node index.js",
"dev": "nodemon index.js",
"build": "webpack --mode production",
"test": "jest"
},
"dependencies": {
"express": "^4.18.2"
},
"devDependencies": {
"nodemon": "^3.0.1",
"jest": "^29.7.0"
}
}
dependencies vs devDependencies
dependenciesâ Packages required at runtime in production (e.g., express, react, axios).devDependenciesâ Packages needed only during development (e.g., jest, webpack, prettier). These are excluded when deploying withnpm install --production.
Semantic Versioning (SemVer)
NPM follows the SemVer specification: MAJOR.MINOR.PATCH.
- MAJOR â Incompatible API changes (breaking)
- MINOR â New backward-compatible features
- PATCH â Backward-compatible bug fixes
Version range symbols in package.json:
| Symbol | Example | Meaning |
|---|---|---|
^ (caret) | ^4.18.2 | Allows updates within 4.x.x (does not cross major) |
~ (tilde) | ~4.18.2 | Allows updates within 4.18.x (does not cross minor) |
* (wildcard) | * | Any version (not recommended for production) |
| None | 4.18.2 | Exact pinned version |
What Is NVM (Node Version Manager)?
NVM (Node Version Manager) is a tool that lets you install and manage multiple Node.js versions on a single machine and switch between them with a single command.
Why Manage Multiple Node.js Versions?
In real-world development, you often maintain several projects simultaneously:
- Project A requires Node.js 16 (a legacy dependency does not support newer versions)
- Project B uses Node.js 20 LTS
- Project C needs Node.js 22 to test new runtime features
Without a version manager, switching Node.js versions means uninstalling and reinstalling â a tedious and error-prone process. NVM makes version switching instant.
How NVM Works
NVM installs each Node.js version under the user’s home directory (~/.nvm/versions/). Each version has its own node and npm binaries, completely isolated from one another. When you run nvm use <version>, NVM updates your shell’s PATH to point to the selected version’s binaries.
Because NVM operates in user space, you never need sudo for installations â eliminating an entire class of permission issues.
NVM Essential Commands
# Check NVM version
nvm --version
# List all available remote versions
nvm ls-remote
# List only LTS versions
nvm ls-remote --lts
# Install the latest LTS version
nvm install --lts
# Install a specific version
nvm install 20.10.0
# List locally installed versions
nvm ls
# Switch to a specific version
nvm use 20
# Switch to the latest LTS
nvm use --lts
# Set default version (used by new shell sessions)
nvm alias default 20
# Show the currently active version
nvm current
# Uninstall a specific version
nvm uninstall 16
Installation Flow: The Right Order
The recommended installation sequence is: NVM first â Use NVM to install Node.js â NPM comes bundled automatically.
Avoid downloading Node.js directly from the official website. That approach makes version switching difficult and can cause global permission conflicts.
Step 1: Install NVM
macOS / Linux:
# Install NVM using the official script (check GitHub for latest version)
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
# Reload your shell configuration
source ~/.bashrc # For Bash
source ~/.zshrc # For Zsh (macOS default)
# Verify installation
nvm --version
The install script automatically adds NVM initialization code to your .bashrc or .zshrc.
Windows:
Windows users should use nvm-windows, a separate project with similar commands but a native Windows implementation.
Step 2: Install Node.js via NVM
# Install the latest LTS version (recommended)
nvm install --lts
# Install a specific major version
nvm install 20
# Set it as the default
nvm alias default 20
# Verify
node -v
npm -v
Step 3: NPM Is Automatically Installed
Once Node.js is installed, NPM is ready to use â no extra steps required:
# Check npm version
npm -v
# Optionally update npm to the latest
npm install -g npm@latest
npm install vs npm ci
Both commands install dependencies, but they serve different purposes:
| Aspect | npm install | npm ci |
|---|---|---|
| Source of truth | package.json + lock file | Lock file only |
| Lock file mismatch | Updates lock file | Throws error and exits |
| node_modules | Incremental update (preserves existing) | Deletes and reinstalls from scratch |
| Best for | Development | CI/CD pipelines |
| Reproducibility | May vary if lock file updates | Fully reproducible |
# Development â install and potentially update lock file
npm install
# CI/CD pipeline â strict, reproducible install
npm ci
Team best practice: Use npm install during development and commit any package-lock.json changes. In CI/CD, always use npm ci to guarantee deployment consistency.
What About npx?
npx ships with NPM 5.2+ and executes package binaries without requiring global installation:
# Scaffold a new Next.js app without installing create-next-app globally
npx create-next-app my-app
# Run the project-local version of ESLint
npx eslint .
# Test your script against a specific Node.js version
npx node@18 script.js
npx lookup order:
- Check local
node_modules/.bin/for the binary - If not found, download temporarily, execute, then clean up
This eliminates version conflicts from global installs and avoids permanently installing tools you rarely use.
CommonJS vs ES Modules
Node.js supports two module systems. Understanding the differences is essential for architecting new projects and reading open-source code.
| Aspect | CommonJS (CJS) | ES Modules (ESM) |
|---|---|---|
| Import syntax | const m = require('./m') | import m from './m' |
| Export syntax | module.exports = ... | export default ... / export { } |
| Loading | Synchronous | Asynchronous |
| Resolution timing | Runtime | Static analysis (compile time) |
| Tree shaking | Not supported | Supported (bundlers remove unused code) |
| Top-level await | Not supported | Supported |
| Conditional imports | Anywhere (if blocks) | Only via dynamic import() |
| Browser support | Not supported (requires bundling) | Native <script type="module"> |
| Recommendation | Legacy project maintenance | Use for all new projects |
How to Enable ES Modules
// package.json â add "type": "module"
{
"name": "my-project",
"type": "module"
}
With this setting, all .js files use ESM by default. If specific files need CommonJS, use the .cjs extension.
// ESM syntax example
import express from 'express';
import { readFile } from 'fs/promises';
export function handler(req, res) {
// ...
}
Using .nvmrc for Team Consistency
A .nvmrc file is a simple text file placed at the project root containing the required Node.js version. When team members clone the repository, they run nvm use in the project directory, and NVM automatically reads .nvmrc and switches to the correct version.
Setting Up .nvmrc
# Create .nvmrc with the desired version
echo "20.10.0" > .nvmrc
# Or use an alias
echo "lts/iron" > .nvmrc
# Commit it to version control
git add .nvmrc
Automatic Version Switching
You can configure your shell to automatically run nvm use when you cd into a directory containing a .nvmrc file. Add this to your ~/.zshrc:
# Auto-switch Node.js version on directory change
autoload -U add-zsh-hook
load-nvmrc() {
local nvmrc_path="$(nvm_find_nvmrc)"
if [ -n "$nvmrc_path" ]; then
local nvmrc_node_version=$(nvm version "$(cat "${nvmrc_path}")")
if [ "$nvmrc_node_version" = "N/A" ]; then
nvm install
elif [ "$nvmrc_node_version" != "$(nvm version)" ]; then
nvm use
fi
fi
}
add-zsh-hook chpwd load-nvmrc
load-nvmrc
.nvmrc in CI/CD
Most CI/CD platforms (GitHub Actions, GitLab CI, CircleCI) support .nvmrc natively or via setup actions:
# GitHub Actions example
- uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
This ensures your CI environment runs the exact same Node.js version as your development machines â no more “works on my machine” issues.
Summary
Understanding the relationship between NVM, NPM, and Node.js is fundamental for any JavaScript developer:
- Install NVM first â it gives you full control over Node.js versions without permission headaches.
- Use NVM to install Node.js â pick the LTS version for production stability.
- NPM comes free with Node.js â use it to manage project dependencies, run scripts, and publish packages.
- Add a
.nvmrcto every project for team and CI/CD consistency. - Use
npm ciin automated pipelines for reproducible builds. - Prefer ES Modules for new projects to benefit from tree shaking and modern syntax.
By following this installation flow and understanding the distinct roles of each tool, you will avoid common pitfalls and set up a reliable, maintainable JavaScript development environment.
Further reading:
Software developer passionate about technology. Sharing programming experiences and learning notes.