Stop Installing Packages Globally

By  on  

These days, most front-end projects are going to involve NPM packages of some kind. Occasionally, when browsing documentation for these packages, I’ll see a recommendation to install a package like this.

yarn global add <package>

Or like this.

npm install --global <package>

In both of these examples, the package is installed globally. This means you can run the <package> command from any directory on your system.

This works, but installing packages globally has a couple downsides.

  • If you’re working with a team of developers, it’s hard to guarantee everyone is running the same package.
  • You can only have one version installed globally. This causes problems if you have different projects that rely on different versions of a package.

In this article, I’m going to show you three different approaches you can use to run packages without having to install them globally.

Quick Setup

For this article, we’re going to install a small CLI tool called Figlet, which prints ASCII art text. Create an empty directory and navigate into it. Then add a package.json file with the following:

  "name": "example",
  "license": "UNLICENSED",
  "dependencies": {
    "figlet-cli": "^0.1.0"

Run yarn install or npm install (depending on your preference) to install the package.

Note: The yarn and npm commands are identical from here on out, so I’m only going to list the yarn versions.

Editing Your $PATH

The first way to run locally install packages as if they’re globally installed is by editing your $PATH environment variable. The $PATH variable tells your system which directories to look for executables in.

One of the handy features of Yarn and NPM is that they both include a .bin directory inside of node_modules that contains symbolic links to all of the installed executables. You can easily add this folder to your path. The trick here is to modify your $PATH to include a local node_modules/.bin directory. This will allow you to run any local NPM CLI tool as if it were installed globally.

First, you need to determine which shell you’re running. To do that, you can type the following into your CLI.

echo $SHELL

If you haven’t configured a custom shell, this will likely be zsh or bash. If it’s bash, open up the ~/.bash_profile file. If it’s zsh, open ~/.zshenv. If the file you need doesn’t exist, then create it.

Next, add the following to the bottom. Notice that ./node_modules/.bin is a relative path. This means it’s appended to whatever directory you’re currently in.

export PATH="./node_modules/.bin:$PATH"

That’s it! Restart your shell, navigate into the directory you created, and try running figlet.

figlet Aww yeah

You should see something like this. Pretty neat, right?

     _                      __   __         _
    / \__      ____      __ \ \ / /__  __ _| |__
   / _ \ \ /\ / /\ \ /\ / /  \ V / _ \/ _` | '_ \
  / ___ \ V  V /  \ V  V /    | |  __/ (_| | | | |
 /_/   \_\_/\_/    \_/\_/     |_|\___|\__,_|_| |_|

Running tools with Yarn

Next up is defining commands in your package.json. To add a command, all you have to do is add a scripts section with your command name and what you’d like to run. In this example, I’ve added an aww-yeah command.

  "name": "example",
  "license": "UNLICENSED",
  "dependencies": {
    "figlet-cli": "^0.1.0"
  "scripts": {
    "aww-yeah": "figlet Aww Yeah"

You can run your custom command with yarn run <command>. Most commands can also be shortened to yarn <command>. Try it with yarn aww-yeah!

You can even pass arguments to your custom commands. Try adding the ascii command listed below to your scripts and running yarn ascii Aww Yeah.

"scripts": {
  "aww-yeah": "figlet Aww Yeah",
  "ascii": "figlet"

Here’s a real-world example. I’m a big fan of both ESLint and Jest. Almost all of my projects have these commands defined in them.

"scripts": {
  "lint": "eslint --max-warnings=0 .",
  "test": "jest"

This is great because my team and I can all share these commands. They’re also self-documenting, so if someone is new to a package they can glance at the package.json to see which commands are available.


Finally, we have NPX, a package runner by the folks from NPM. This handy tool allows you to run CLI commands without installing a package locally. This is great for tools that you only need to run once, such as generators.

NPX is likely already installed on your machine if you’ve installed Node.js. If not you can install this one globally with yarn global add npx.

Let’s give it a shot with figlet.

npx figlet Aww Yeah

Wasn’t that easy?

Occasionally, you’ll run into a command that NPX doesn’t know how to find. An example is my Yeoman Generators repository. In those cases, you’ll need to tell NPX which package to run explicitly with a -p flag.

npx -p yo -p @landonschropp/generator-eslint yo @landonschropp/eslint

All Done!

And there you have it. Now, you can install any NPM module locally and run the command as if it were global. I personally use all three of these methods on a regular basis. I hope you find them as useful as I have!

Landon Schropp

About Landon Schropp

Landon is a developer, designer and entrepreneur based in Kansas City. He's the author of the Unraveling Flexbox. He's passionate about building simple apps people love to use.

Recent Features

  • By
    Convert XML to JSON with JavaScript

    If you follow me on Twitter, you know that I've been working on a super top secret mobile application using Appcelerator Titanium.  The experience has been great:  using JavaScript to create easy to write, easy to test, native mobile apps has been fun.  My...

  • By
    Responsive Images: The Ultimate Guide

    Chances are that any Web designers using our Ghostlab browser testing app, which allows seamless testing across all devices simultaneously, will have worked with responsive design in some shape or form. And as today's websites and devices become ever more varied, a plethora of responsive images...

Incredible Demos

  • By
    CSS @supports

    Feature detection via JavaScript is a client side best practice and for all the right reasons, but unfortunately that same functionality hasn't been available within CSS.  What we end up doing is repeating the same properties multiple times with each browser prefix.  Yuck.  Another thing we...

  • By
    From Webcam to Animated GIF: the Secret Behind!

    My team mate Edna Piranha is not only an awesome hacker; she's also a fantastic philosopher! Communication and online interactions is a subject that has kept her mind busy for a long time, and it has also resulted in a bunch of interesting experimental projects...


  1. Yarn also allows you to run any binary defined in any of the locally installed packages, so you don’t have to alias the binary in scripts. yarn figlet works directly!

  2. Nico

    Don‘t put relative paths in $PATH. This might also overwrite commands like ls or rm. Might be an annoyance or a security risk. Using npx is explicit and better.

    By the way, you got hungry in the middle, but nom install won‘t work ;)

    • This might be technically true, but I’ve never actually had this happen. Usually, with packages such as ESLint, I want the local version to override the global version. But you should definitely set up your environment to your own preferences and your own level of comfort.

      And don’t worry, I had a snack and I’m not hungry now.

    • That’s right. Putting relative paths in $PATH makes the commands globally. And I don’t see any benefit from that than installing the packages globally.

      Unless the environment is shared (like a server), this behavior is the same. If there’s a conflict, upgrading global package should be done.

  3. Bogdan Luca

    In the meantime, I guess figlet maintainers extracted the command to a separate package – figlet-cli, so npx figlet doesn’t work anymore, it should be, like you showed below with Yeoman, npx -p figlet-cli figlet.

Wrap your code in <pre class="{language}"></pre> tags, link to a GitHub gist, JSFiddle fiddle, or CodePen pen to embed!