Skip to content

What Are NPM Scripts?

An npm script is a convenient way to bundle common shell commands for your project. They are typically commands, or a string of commands, which would normally be entered at the command line in order to do something with your application.

Scripts are stored in a project’s package.json file, which means they’re shared amongst everyone using the codebase. They help automate repetitive tasks, and mean having to learn fewer tools. Node npm scripts also ensure that everyone is using the same command with the same flags.

Common use cases for npm scripts include building your Node project, starting a development server, compiling CSS, linting, minifying, or anything else you find yourself typing into your terminal frequently that’s related to your project.

In this tutorial we’ll:

  • Learn what an npm script is in Node
  • Learn how to run scripts with npm
  • Explain the difference between custom and default scripts

By the end of this tutorial, you’ll know what npm scripts are, and when you might want to use them with your project.

Goal

Understand what npm scripts are, and the use case for including them in a project’s package.json file.

Prerequisites

Watch: What are NPM scripts?

What are npm scripts?

NPM scripts are terminal commands that run in your local shell with a bit of additional syntactic sugar. Really, that’s all there is to it. 🎉

Scripts are stored in a projects package.json file, which means they’re shared amongst everyone on a project. Meaning that if you use scripts to run your common tasks you can also be sure everyone on the project is using the same set of flags and arguments.

This also means that when writing scripts you should write them in a way that’s sure to work for everyone. For example; Don’t hard code file paths to your local environment.

Scripts are executed using the npm run {SCRIPT_NAME} command. There are also some special predefined aliases that convert to the npm run version. For example; npm test converts to npm run test, and the two can be used interchangeably.

You can also write pre- and post-hooks, which are scripts that can run before certain life cycle events in your project. For example the postinstall script will be triggered automatically after a package is installed using npm install {PACKAGE} and could be used to perform additional build steps. See a full list of supported scripts.

A real world example

It’s common to have commands that you run in order to do things like start your application, run code linting, or execute your applications test suite. Often these commands can get long and detailed with lots of options with vague values making them hard to remember. Not to mention, the need for everyone on the team to run the command with the same set of options or risk all kinds of conflicts.

Rather than make everyone remember exactly how to invoke the command to start the application, and to help speed up onboarding for people to the project, it would be convenient to provide an easy and memorable way for someone to start the application. This is where npm scripts come in handy.

Example package.json entry:

...
"scripts": {
"start": "node index.js"
},
...

A common npm start script is something like node index.js. And maybe your project needs some additional flags to be set, for example: node index.js --port=8000 --debug=false.

Commands that are registered as scripts in a package.json file are also included in version control. You can do things like test different permutations of the command in different branches. And maintain a history of how the command used to start the application has changed over time. Both of which can be handy for debugging.

You can run the npm run command without any additional arguments to see the complete list of scripts defined for a package at any time.

Example from a different package.json:

Terminal window
npm run
available via `npm run-script`:
build
gatsby build
develop
gatsby develop
format
prettier --write src/**/*.{js,jsx}
lint
eslint ./src
serve
gatsby serve

Default scripts

For some scripts, if your project doesn’t define a specific command to run npm will use a default. For example; If your project doesn’t have a start script defined, npm start will by default try and run node server.js, even if that file doesn’t exist.

Tip: Don’t have a start script and want to avoid the default behavior? Use something like the following:

Example package.json entry:

...
"scripts": {
"start": "echo \"Not implemented\""
},
...

Another common use case is stubbing out scripts with some default values in scenarios where you expect someone to copy your project.json file into their own project.

Example:

"scripts": {
"test": "echo \"Write tests! -> https://gatsby.dev/unit-testing\""
},

Compose scripts

Scripts can call other scripts. Here’s an example where we add a linting step into our dev workflow. This will run the linter and restart the project on each file save.

Example package.json entry:

...
"scripts": {
"start": "node index.js",
"dev": "NODE_ENV=development npm run lint && npm start",
"lint": "eslint ./*.js",
},
...

When **npm run dev** is called, here’s what happens:

  1. Set the NODE_ENV environment variable to development
  2. Run the lint script, and if successful, run npm start
  3. The lint script will call eslint
  4. Finally, start will start the project

And, voila! Anyone working with this project can run the development environment just by typing npm run dev!

Of course, any collaborators would need to have eslint installed for this to work for them. In order to make certain your npm scripts will work for everyone, list any required applications as either dependencies or devDependencies in your package.json. That way you can be sure everyone has them installed.

Scripts have the ability to access commands provided by installed packages by name, instead of requiring a complete path. They are smart about resolving to a locally-installed package instead of a globally-installed one. Did you notice how in the above lint script the eslint command is called without having to specify relative path to node_modules/eslint/ or equivalent? The npm run command will first try and resolve eslint to a locally-installed package like node_modules/eslint/bin/eslint for example, then fallback to a globally-installed one if it’s not available.

Recap

NPM scripts are a powerful way to bundle aliases for commonly executed commands right in your project. This makes it easier for collaborators to discover, and ensures you don’t have to remember all the vague command line flags required for your specific use case. Script definitions are stored in the scripts key of your projects package.json file, and are executed via the npm run {SCRIPT_NAME} command.

Further your understanding

  • Run the command npm help run to learn more about scripts in the package.json file
  • Explore some of the existing Node packages you use. Open their package.json and look for a scripts entry. What’s there? What does it do?

Additional resources