Mighty Practices

Use Mise to manage tooling versions

A guide on using Mise-en-place for managing versions of tools like Node, NPM, and Terraform in your development environment.

Overview

Keeping versions of development tools straight is hard when working across multiple projects. Running the wrong version can lead to "works-on-my-machine" errors, and lost time troubleshooting. There are many tools that manage versions of specific tools, but it can be a hassle to use and maintain multiple version managers for different tools.

Mise-en-place (or simply mise) is a CLI tool that manages versions of many different tools on a per-project basis. With a single mise.toml file, you can specify the versions of Node, NPM, Terraform, and more that your project requires. When you navigate to the project directory, mise automatically switches to the correct versions of those tools.

We recommend using mise on all projects as a no-fuss way to ensure everyone is on the same toolset.

Installing and configuring Mise

To install mise, follow the installation instructions on the Mise-en-place website. Specifically:

  1. Install the mise binary:
    brew install mise
  2. Activate mise in your shell by adding the following line to your shell configuration file (e.g., .bashrc, .zshrc):
    echo 'eval "$(mise activate zsh)"' >> ~/.zshrc
    source ~/.zshrc

Using mise for a project that's already configured

As long as you have mise installed and activated in your shell, when you change directory into any project that has a mise.toml file, mise will first ask you if you want to trust the mise.toml file (this is a security measure).

Assuming you choose to trust the file, mise will automatically install any missing versions of tools specified in the mise.toml file and switch to those versions for that project.

For example, if your project has a mise.toml file that looks like this:

mise.toml
[tools]
node = "24.13.0"
pnpm = "11.3.0"

When you navigate to that project directory and run node --version, mise will automatically download and switch Node 24.13.0, which will produce the output:

$ node --version
v24.13.0

Configuring mise

There's a lot that mise can do. We'll give a lightning tour of some of the more useful features, but for more details, check out the Mise-en-place documentation.

Tool version management

This is the core feature of mise. You can specify versions of tools that your project depends on, and mise will automatically switch to those versions when you navigate to the project directory.

# Create or updates the mise.toml file with the latest 24.x version of node
mise use node@24
# Create or updates the mise.toml file with the latest 11.x version of pnpm
mise use pnpm@11
# Create or updates the mise.toml file with the latest version of OpenTofu
mise use opentofu@latest

Every mise use command will result in a write to the mise.toml file, which will look something like this:

mise.toml
[tools]
node = "24.13.0"
pnpm = "11.3.0"
opentofu = "1.9.1"

You can also set global versions of any tools:

# Set the global version of node to the latest 24.x version
mise use -g node@24

Finally, by enabling idiomatic version files: mise can read from existing version files like .nvmrc, .node-version, .terraform-version, etc. This allows you to use mise without having to migrate your existing version files.

mise has a crazy amount of supported tools. For Mighty Acorn, the highlights are:

ToolUse
Nodemise use node@latest
NPMmise use npm@latest
PNPMmise use pnpm@latest
Terraformmise use terraform@latest
OpenTofumise use opentofu@latest

While mise has many, many more packages available, you should continue to use npm for node package management, and brew for OS-level global tool installation. mise is not a replacement for those tools, but rather a complement to them.

Task Running

The final major feature of mise is task running. You can define custom tasks in your mise.toml file, and then run those tasks with mise run <task-name>. Some examples

mise.toml
[tasks.build] # run with `mise run build`
# Tasks can have descriptions, shown when running `mise tasks`.
description = "Builds the project by running both the site and storybook builds."
# Run can be either a single command or an array of commands. Arrays will run each command sequentially.
run = [
    "echo 'Building storybook...'",
    "echo 'Building the site...'",
]

[tasks.deploy] # run with `mise run deploy <environment>`
# Tasks can have arguments, defined in the usage section.
usage = '''
arg "<environment>" help="Target environment" {
  choices "dev" "staging" "prod"
}
'''
# Arguments can be passed to the run section using {{usage.<arg_name>}} syntax.
run = 'echo "Deploying the project to ${usage_environment?}..."'

[tasks.release] # run with `mise run release`
# Tasks can have dependencies on other tasks, which will be executed before the task itself.
depends = [
    "build",
]
# Run steps can also reference tasks, and optionally pass arguments.
run = [
    { task = "deploy prod" },
]

The use case for mise tasks is somewhere in between npm scripts and shell scripts. If you have simple tasks that just run a single command, npm scripts are simpler and more portable. If you have complex tasks that require conditionals, loops, or other logic, shell scripts might be more appropriate.

Environment Variable Management

Warning

Environment variables in mise are evaluated when the prompt is shown, not when you run a command. If you change variables, then run a command, you'll see the old values, not the new ones. To refresh environment variables, hit enter to show the prompt again, and then run your command. This is confusing behavior, so use this feature with caution.

mise can also serve as an environment variable manager. Variables set in the env section of mise.toml will be set when you navigate to that project. This can be useful in a few scenarios:

mise.toml
[env]
# Load `.env` files in the shell (replaces `dotenv`)
_.file = ".env"
# Committing non-secret environment variables to source control:
NODE_ENV = "development"
# Giving a warning if certain environment variables aren't set:
AWS_PROFILE = { required = true }
# Adding to `$PATH` so project-level packages can be used without `npx`:
_.path = ['{{config_root}}/node_modules/.bin']

Summary: When should I use mise?

We recommend using mise for declaring and installing versions of language runtimes for your project. It doesn't replace npm or brew, but is the right choice for managing versions of node, npm, terraform and other tools that may differ from project to project.

We conditionally recommend mise for medium-complexity scripting tasks. Simple commands should still be written as npm scripts. Complex commands should be written as shell scripts. Use mise for stuff in between.

We conditionally recommend mise for environment variable management to replace code-level dotenv usage, but be aware of the gotchas around when variables are evaluated.

Help us improve

Your feedback helps us create better resources for teams like yours.

Was this page helpful?

Last updated on