Node.js version management is less about installing Node and more about orchestrating dependencies across projects.

Let’s see nvm in action. Imagine you have two projects: project-a which needs Node 14, and project-b which needs Node 18.

First, install nvm itself. Assuming you’re on macOS with Homebrew:

brew install nvm

This command installs nvm but doesn’t automatically set it up for your shell. You’ll need to add a few lines to your shell’s configuration file (like ~/.zshrc or ~/.bash_profile). Homebrew usually prints these instructions after installation. It’ll look something like this:

export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"  # This loads nvm
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"  # This loads nvm bash_completion

After adding these lines and sourcing your shell config (e.g., source ~/.zshrc), you can start using nvm.

Now, let’s install Node 14 and Node 18:

nvm install 14
nvm install 18

nvm downloads and compiles these specific Node.js versions. You’ll see output indicating the download and installation progress.

To switch to Node 14 for project-a:

nvm use 14

And to switch to Node 18 for project-b:

nvm use 18

You can verify your current Node.js version with node -v. Each nvm use command changes this globally for your current shell session.

To make a version the default for new shell sessions, use nvm alias default <version>:

nvm alias default 18

This ensures that when you open a new terminal window, Node 18 will be active by default.

The core problem nvm solves is the friction of maintaining multiple Node.js environments for different projects. Without it, you’d be manually downloading, installing, and trying to symlink different Node versions, which is error-prone and cumbersome. nvm provides a clean, isolated way to manage these versions.

Internally, nvm works by creating a directory (usually ~/.nvm) and placing each installed Node.js version within a subdirectory there (e.g., ~/.nvm/versions/node/v14.21.3/). When you run nvm use <version>, it manipulates your shell’s PATH environment variable to point to the bin directory of the selected Node.js version. This effectively makes that version the one your shell finds and executes when you type node or npm.

The nvm list command shows you all installed versions, with an asterisk next to the currently active one.

nvm list

Output might look like:

-> v18.17.1
   v14.21.3
default -> 18.17.1 (-> v18.17.1)

This clearly indicates that v18.17.1 is currently in use.

When you run nvm install <version>, it downloads the pre-compiled binaries for that version from Node.js’s official distribution and places them in ~/.nvm/versions/node/. If pre-compiled binaries aren’t available for your architecture, nvm will attempt to compile from source, which requires development tools like GCC or Clang.

The nvm use command is a shell function, not an executable. This is crucial because it needs to modify the PATH of your current shell. If nvm were a standalone executable, it would only modify its own environment, and those changes wouldn’t propagate back to your interactive shell. The sourcing of nvm.sh in your shell profile ensures this function is available.

A common point of confusion is that nvm use is session-specific. If you close your terminal and open a new one, nvm doesn’t automatically know which version you intended to use for a particular project. This is where .nvmrc files come in. You can create a file named .nvmrc in the root of your project directory containing just the version number (e.g., 18 or lts/hydrogen). Then, when you cd into that directory, you can run nvm use (without arguments) and nvm will automatically pick up the version from the .nvmrc file. Some shells can be configured to automatically run nvm use when you cd into a directory with an .nvmrc file.

The nvm install --lts command installs the latest Long Term Support version of Node.js. This is generally recommended for production environments due to its stability and extended support period.

To uninstall a Node.js version:

nvm uninstall 14

This removes the corresponding directory from ~/.nvm/versions/node/.

The next logical step in managing your Node.js ecosystem is understanding how package managers like Yarn and pnpm interact with different Node.js versions and how to handle global npm packages across them.

Want structured learning?

Take the full Homebrew course →