avatar
Published on

Automatically Switch Node Versions with nvm for React Native Projects

Authors
  • avatar
    Name
    Mick MacCallum
    Twitter
    @0x7fs

If you work on multiple React Native or Expo projects, you've probably hit build errors caused by using the wrong Node version. Maybe the Metro bundler crashes, or pod install fails with some obscure Ruby error, or your CI passes but your local build doesn't. Often the culprit is a Node version mismatch.

The fix is to lock your project to a specific Node version and have your shell switch to it automatically.

The Problem

React Native and Expo projects can be picky about Node versions. Common symptoms of version mismatches:

  • ENOENT or EACCES errors during npm install
  • Metro bundler crashes on startup
  • Native build failures that work fine in CI
  • "Unsupported engine" warnings you've been ignoring
  • Mysterious differences between team members' machines

The root cause is usually that your global Node version doesn't match what the project expects.

Installing nvm

If you're not already using nvm, it's a tool that lets you install and switch between multiple Node versions on the same machine. Install it with:

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

Then restart your terminal. You can verify it's working with nvm --version. See the nvm GitHub repo for more details.

Step 1: Add an .nvmrc File

Create a .nvmrc file in your project root with the Node version you want:

echo "20" > .nvmrc

Or be more specific:

echo "20.11.0" > .nvmrc

This file tells nvm which version to use, but by default you still have to run nvm use manually every time you open the project.

Step 2: Auto-Switch on Directory Change

Add this to your ~/.zshrc after your nvm initialization:

# Auto-switch Node version based on .nvmrc
autoload -U add-zsh-hook

load-nvmrc() {
  local nvmrc_path
  nvmrc_path="$(nvm_find_nvmrc)"

  if [ -n "$nvmrc_path" ]; then
    local nvmrc_node_version
    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
  elif [ -n "$(PWD=$OLDPWD nvm_find_nvmrc)" ] && [ "$(nvm version)" != "$(nvm version default)" ]; then
    echo "Reverting to nvm default version"
    nvm use default
  fi
}

add-zsh-hook chpwd load-nvmrc
load-nvmrc

Now when you cd into a directory with an .nvmrc, nvm automatically switches to that version. If the version isn't installed, it installs it for you. When you leave the directory, it reverts to your default.

Reload your shell config:

source ~/.zshrc

Step 3: Enforce the Version in package.json

The .nvmrc handles local development, but you should also document the requirement in package.json so npm and yarn can warn users:

{
  "engines": {
    "node": ">=20.0.0"
  }
}

This won't block installs by default, but it will show a warning. To make npm strict about it, users can set:

npm config set engine-strict true

Step 4: Lock the Package Manager Too

Different versions of npm can produce different package-lock.json files, leading to "works on my machine" issues. The packageManager field in package.json locks this down:

{
  "packageManager": "npm@10.2.4"
}

With Corepack enabled (it ships with Node but is off by default), this ensures everyone uses the exact same npm version:

corepack enable

Now if someone tries to run npm install with the wrong version, they'll get an error instead of a subtly different lockfile.

Putting It Together

A complete setup looks like this:

.nvmrc:

20.11.0

package.json:

{
  "name": "my-app",
  "engines": {
    "node": ">=20.0.0"
  },
  "packageManager": "npm@10.2.4"
}

Add both files to version control. New team members clone the repo, cd into it, and nvm handles the rest. No more "what Node version are you on?" in Slack.

Expo and React Native Specific Notes

Expo's managed workflow is generally more forgiving about Node versions, but the bare workflow and standalone React Native projects often aren't. Check the React Native environment setup docs for the currently recommended Node version.

If you're using Expo, their requirements page lists supported Node versions. When in doubt, use the latest LTS.