Skip to main content

Command Palette

Search for a command to run...

5 Node Version Managers Compared – Which is Right for You?

Updated
7 min read
5 Node Version Managers Compared – Which is Right for You?
P

Software Engineer.

Focused on Node.js and JavaScript.

Here to share my learnings and to learn something new.

Imagine you joined a Node.js project and want to bootstrap it to see how it goes, but you see an error. What is the problem? After spending some time, you find out that Node.js version you’re using on your machine is not the one that the project requires.

This is quite a common and annoying situation. I have been there myself. To avoid such troubles, smart people developed tooling called “node version manager.” A shell utility that allows you to switch between Node.js versions easily.

This article will focus on the Node.js version managers market. You’ll see how they differ and which one you should consider using.

Criteria

To make comparison easier, we’ll introduce the following criteria:

  • Cross-platform: Is the manager cross-platform?

  • Upfront setup: How much work must you do for the initial installation?

  • Node version sources: From what sources can the Node.js version be parsed?

  • Daily usage: How easy and seamless is using version manager daily?

Contestants

Comparison

With defined criteria, we can now look at each contestant in more detail.

Node Version Manager (NVM)

It is the most popular solution for node version management (at least by GitHub repository stars, 75.2k). The reason for that is its early appearance. It was one of the first, if not the first, Node.js version managers at the time and gained huge popularity in the community.

Is it cross-platform? Not really. It doesn’t have full Windows support. It works in some cases like GitBash (MSYS), Cygwin, and WSL (Windows Subsystem for Linux). There is a separate package for Windows called nvm-windows, but it is not NVM itself.

Another limitation is the support of POSIX shells only, such as bash or zsh, which leaves users of other shells, like Fish, out of the question.

The most straightforward way to install NVM is to run the following command.

curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash

Here is how you use NVM to switch between different Node.js versions.

user@machine:~/project node -v
v21.7.2
user@machine:~/project cat .nvmrc
18.19.1
user@machine:~/project nvm use
user@machine:~/project node -v
v18.19.1

NVM can understand which version of Node.js to use through the .nvmrc file. You must either create one before switching between versions or explicitly declare Node.js version to which you want to switch, e.g. nvm use 18.10.

Notice that running the nvm use command will set the Node.js version for the current shell. What does this mean? Even if you leave the project folder and navigate to another project, the Node.js version will stay the same until you rerun the nvm use command.

It adds more friction to your workflow and creates a greater cognitive load because you must always be aware of what Node.js version your current shell uses and what is required for a particular project.

It is still better than manually managing all possible Node.js versions, but far from seamless integration.

N

N is another popular Node.js version manager (18.5k GitHub starts).

It is not cross-platform and has even more limitations than NVM. It does not work in native shells on Microsoft Windows (like PowerShell), Git for Windows Bash, or with the Cygwin DLL.

N can be installed directly from NPM. Run npm install -g n. It can also be installed with the Brew on macOS or by downloading the sh script.

curl -L https://bit.ly/n-install | bash

One of the big benefits of using N is its ability to detect Node versions directly from the “engines” section. If you have the following package.json structure:

{
  "name": "project",
  "version": "1.0.0",
  "main": "index.js",
  "engines": {
    "node": "18.17.0"
  },
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "license": "ISC"
}

N will install “18.17.0” as you specified in the engines section.

However, N suffers from a similar problem to NVM. If you want to use the exact Node.js version for different projects, you have to keep track of it yourself.

Moreover, N takes this problem to a whole new level. Using N means managing a “global” Node.js version. Even after closing a shell, you’re left with the Node version you used for the latest project — not the best experience.

Fast Node Manager (FNM)

FNM is a node version manager written in Rust. It is as popular as N (15.2k GitHub stars).

FNM is the first cross-platform node version manager on the list. It runs on Windows without the need to install any other packages.

The installation process is clear and intuitive.

# macOS
brew install fnm

# Using rust package manager cargo
cargo - cargo install fnm

# On windows using winget
winget - winget install Schniz.fnm

Or using bash script for Unix-based OS.

curl -fsSL https://fnm.vercel.app/install | bash

FNM manages Node.js version per shell and doesn’t try to build its main workflow through global versioning like N does. It has a “default” version which is global and it plays the role of fallback in case some project doesn’t have a specified Node.js version.

The other cool feature of FNM is the auto-switching of Node.js version based on the folder you’re in, but you have to do some configuration for it.

Auto-switching works in the following way: If you go from one project that uses the 18.17.0 version to a different one with 20.12.1, FNM automatically switches Node.js version after you navigate into the new project folder.

user@machine:~ node -v
V21.7.2
user@machine:~ cd project-1
Using Node.js v18.17.0
user@machine:~/project-1 cat .node-version
18.17.0
user@machine:~/project-1 cd ..
user@machine:~ node -v
V18.17.0
user@machine:~ cd project-2
Using Node.js V20.12.1
user@machine:~/project-2 cat .node-version
v20.12.1

We switched between two projects, and the Node version automatically changed based on the .node-version file inside those projects.

You must install the required Node.js version on a machine to make auto-switching work properly.

The other thing to remember is that it can detect node versions only from extra files you create in a project. Those files are .node-version or .nvmrc.

Volta

Volta is a rising star in the world of version managers (10k starts on GitHub).

It is written in Rust, and it is cross-platform.

The installation process is seamless for Unix-based systems.

curl https://get.volta.sh | bash

Windows has a separate installer.

When you configure Volta's Node.js version, you do not need to create extra files. Volta uses configuration from package.json.

{
  "name": "project",
  "version": "1.0.0",
  "main": "index.js",
  "engines": {
    "node": "18.17.0"
  },
}

The benefit of such a configuration is that the engines section is right next to the Volta configuration. This allows you to keep them in sync effortlessly. When placed in a separate file, it is easy to forget to sync those versions.

Another huge feature is the management of a toolchain. What does it mean?

Imagine that you’re using Yarn as a package manager. Other Node.js version managers can manage only Node.js versions. At the same time, Yarn version can change from project to project.

That is where Volta shines. You can dynamically switch not only the Node.js version but Yarn version as well. Just add Yarn version under the “volta” configuration section.

  "volta": {
    "node": "18.17.0",
    "yarn": "1.22.22"
  }

Whenever you run the install command, Volta, ensure that Node.js and Yarn versions match the declared one. Isn’t it magical?

PNPM

Don’t be surprised. PNPM is usually perceived as an alternative to package managers like NPM and Yarn. However, unlike those, PNPM can manage Node.js version itself.

PNPM is cross-platform and provides the same Node.js version management experience throughout all platforms.

However, there are four downsides to using PNPM as a Node version manager.

The first one is that PNPM is not a Node version manager at its core. It is a package manager that can manage the Node.js version. You can’t easily use it with other package managers like NPM or Yarn.

The second one is that Node.js installed using PNPM is not shipped with Corepack. Here is the note from the documentation:

PNPM env does not include the binaries for Corepack. If you want to use Corepack to install other package managers, you need to install it separately (e.g. PNPM add -g corepack).

The third one is that PNPM can only manage the Node.js version globally. You can’t configure it per-shell. If you try to install without the --global flag, you get the following error message:

"pnpm env use <version>" can only be used with the "--global" option currently

It doesn’t switch Node.js version dynamically as you navigate from project to project. This means you have to track it all yourself and ensure it matches the version required for a project.

Conclusion

The Node.js version managers have come a long way. NVM was the first and most popular solution for quite a long time and remains one.

But the ecosystem is evolving. Over time, different tools, such as N, FNM, and Volta, emerged. Each has pros and cons.

At this point, Volta seems to be our most feature-reach and complete Node.js version manager. It is cross-platform, provides a seamless experience in day-to-day usage, and takes care of other tools you use on the project.

L

Excellent comparison of Node.js version managers! It really shows the effort needed for environment setup. I've found ServBay simplifies my entire local dev environment, letting me focus on development instead of managing versions. Super helpful analysis!

D

I use mise-en-place so I don't need a different tool for other languages, it's fast and very good. The broader standard of a asdf style .tool-versions is nice for consistency between e.g. Python and Node.

S

Hi there! So... I've been using FNM for a couple of years already. And I looked into it, and it seems that there's experimental support for the "engines" prop of the package.json file: https://github.com/Schniz/fnm/pull/839

Just wanted to point that out

1
P

Hey Santiago, I appreciate the read!

Wow, that would be a huge change. I would even consider switching to FNM if they can stabilize the feature.

In my opinion, it is the best way to manage the Node version.

A

You should also compare mise which manages node, neovim, python, etc for me and can be used for many more tools.

https:// mise.jdx.dev/

I'm not sure why it doesn't catches the conversation, maybe because they don't advertise it as "node version manager" or "python version manager" which is what people mostly search for. Instead, it is "almost anything version manager" and can be extended to support currently unsupported softwares by creating plugins.

B

As a long-time user of nvm and then n, I would like to add the crucial difference between those two, and why I prefer n over nvm. nvm works through usage of environment variables, changing the path for the node executable file. While this can be enough for most users, I've encountered problems in integrated shells in IDEs (namely in Sublime Text 2, back in the day). Back then, the IDE spawned a shell with a clean and minimal environment (using something like env -i), thus wiping any nvm configurations from the session. n uses symbolic links to manage node and npm versions, thus being a little more robust to differences in environments.

1
P

I appreciate the input, Bernardo!

Is it still a problem with modern-day IDEs?

T

Surprised to see no mention of nodenv 🤔

1
P

Thanks for the read Tushar!

There are just too many version managers to mention all of them in one article. That’s why I picked just five.

Maybe I'll post one or two more articles to review more of them. But I bet that they won't cover all of the managers either.

D

I also thought you could use .nvmrc to set node version within a project. You can even have your bash/zsh/fish automatically run nvm use on cd commands so it automatically picks up the version.

While you say that n is even more limited, it does work with fish, which is why I use it over all others and have for years. It also has some support for .nvmrc so that you can use n auto while others use nvm use.

1
P

Hey David, thank you for reading the article. I appreciate it!

You're right. We can use the .nvmrc file to declare the Node version for specific projects. And I'm mentioning it in the article.

You're also right that we can configure shell run run nvm use on cd. However, the reason why I decided not to include it in the article is that it is not maintained by the NVM team. To me, it looks more like a community attempt to fix the initial problem in the same way as nvm-widows doing it.

That's right, N does work with Fish as all of the other managers in this list except NVM by default.

Do you use it with .nvmrc? For me, the best part about N is the ability to detect the Node versions just based on the engines section of the package.json file.

R

If you need anything about crypto recovery just contact (bensoncryptorecoveryexpert@gmail.com) I have never seen such tech skills in my life before, They helped me recover my $95,000 btc i invested in a forex platform and couldn’t withdraw it. I contacted them via the above email address and everything worked out fine. They did exactly as they said.

C

It doesn’t switch Node.js version dynamically as you navigate from project to project.

This is somewhat correct for shells but not for projects. Using the use-node-version property in a .npmrc file forces PNPM to use that version of Node in its execution context and auto-installs the declared version of Node if not previously downloaded. You can have nested .npmrc files (think monorepo) to use different versions of Node for targeted projects all isolated from each other as well as your global version.

This means your global Node version (set by PNPM) does not affect your locally declared Node version and doesn't require any separate configuration or commands, like nvm use, to switch Node versions.

1
P

Hey Christopher, thanks for the read, and I appreciate your input!

I should've added the note of using .npmrc; that's a miss for sure.

As for "doesn't require any separate configuration." We pretty much do have a separate configuration in the .npmrc file.

In my ideal world, every tool that claims to manage the Node.js version should take it right from the engines section of package.json, like N does it.

Volta is pretty close to the ideal since it places configuration right inside of the file, which makes it easier to keep those sections in sync.

More from this blog

Pavel Romanov

26 posts

Software Engineer.

Focused on Node.js and JavaScript.

Here to share my learnings and learn something new.