# npm and Packages

npm is an app store for code that comes with Node.js; you'll describe a project in package.json, install packages into node_modules, tell dependencies from devDependencies, and run the project's commands as npm scripts.

Journeyman · JavaScript · Node.js · Software Development · Updated November 1, 2026

## After this lesson you can

- Install packages with npm and tell dependencies from devDependencies in package.json
- Explain what node_modules and package-lock.json are for and which of them to commit
- Add an npm script to package.json and run it with npm run

## Key takeaways

- A package is code someone published for anyone to use. npm installs packages from its registry with npm install, and lists them in package.json.
- A dependency is a package your app needs when it runs. A devDependency is a tool you only need while you build and test, installed with --save-dev.
- node_modules holds the installed packages and is never committed; .gitignore keeps it out. package-lock.json records exact versions and is committed.
- npm install with no package name rebuilds node_modules from package.json and package-lock.json. It's the first command for any project you download.
- An npm script is a named command in package.json. npm run name runs it, and npm start and npm test work without the word run.

Download almost any JavaScript project, whether it's a starter for a class assignment, an open-source game on GitHub or the code base on your first day at a new job, and its README says the same thing first: run `npm install`. Professional projects are built mostly from code other people wrote and shared, and npm is how that code gets into your project. After this lesson you can set up a project of your own, add other people's code to it, and know which files matter when you share it.

## An app store for code

When you want a flashlight on your phone, you don't build one. You open the app store, search, and install. Programmers work the same way. Need to show a date as *Saturday, November 14*? Fire confetti when someone wins? Draw a chart? Someone has already written that code, tested it, and published it for anyone to use.

A piece of code published to share is called a **package**. **npm** (say each letter: "en-pee-em"; it started as short for *Node Package Manager*) is two things at once:

- **The registry**, at npmjs.com: a free, searchable library of millions of packages.
- **The `npm` command**, which came with Node.js when you installed it. It downloads packages from the registry into your project.

A few packages you might reach for: `date-fns` for dates, `canvas-confetti` for celebrations, `chart.js` for charts, `phaser` for 2D browser games. The tools in the rest of this module (Vite, ESLint, Prettier and Vitest) are npm packages too.

## package.json: the project's ID card

Every npm project has a file named **`package.json`** at its top: the project's ID card. It holds the project's name, its commands and the packages it uses. You met it in *Modules: import and export*, where its `"type": "module"` line made Node read files as modules.

The running example in this chapter is the **Game Night Scoreboard**: three friends, a few rounds of party games, and a program that keeps score. In VS Code, make a folder `game-night` in your course folder, open it, and run this in the terminal (`-y` is read "dash y": it answers yes to every question `npm init` would ask):

```bash
npm init -y
```

npm writes `package.json` and shows it:

```json
{
  "name": "game-night",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "type": "commonjs"
}
```

Change `"commonjs"` to `"module"`, so your files can use `import` and `export`. (If your package.json has no `"type"` line, an older npm made it: add `"type": "module",` yourself.)

## The scoreboard

Two files make the scoreboard. `scores.js` holds the players, the rounds and the adding up:

```javascript
export const players = ["Maya", "Jordan", "Priya"];

export const rounds = [
  { game: "Trivia", points: { Maya: 8, Jordan: 6, Priya: 7 } },
  { game: "Charades", points: { Maya: 5, Jordan: 9, Priya: 6 } },
  { game: "Word Scramble", points: { Maya: 7, Jordan: 4, Priya: 9 } },
];

export function totalFor(player, rounds) {
  return rounds.reduce((sum, round) => sum + (round.points[player] ?? 0), 0);
}

export function standings(players, rounds) {
  return players
    .map((player) => ({ player, total: totalFor(player, rounds) }))
    .sort((a, b) => b.total - a.total);
}
```

`main.js` prints the standings, highest first:

```javascript
import { players, rounds, standings } from "./scores.js";

console.log("Game Night Scoreboard");
for (const { player, total } of standings(players, rounds)) {
  console.log(`${player}: ${total}`);
}
```

`node main.js` prints:

```text
Game Night Scoreboard
Priya: 22
Maya: 20
Jordan: 19
```

## Installing a package

The scoreboard should say which night it's for, written the friendly way: *Saturday, November 14*. JavaScript's own dates can do it, with some work. The `date-fns` package does it in one line. Install it with `npm install` and the package's name:

```bash
npm install date-fns
```

```text
added 1 package, and audited 2 packages in 5s

1 package is looking for funding
  run `npm fund` for details

found 0 vulnerabilities
```

(*Funding* lists packages whose authors accept donations, and *vulnerabilities* counts known security problems in what you installed: 0 is what you want.) Three things changed in the folder: `package.json` has a new `dependencies` section, a `node_modules` folder appeared with the package's code inside, and a file named `package-lock.json` appeared. Now `main.js` can import from the package by its name, with no `./` in front:

```javascript
import { format } from "date-fns";
import { players, rounds, standings } from "./scores.js";

const gameNight = new Date(2026, 10, 14);

console.log(`Game Night Scoreboard: ${format(gameNight, "EEEE, MMMM d")}`);
for (const { player, total } of standings(players, rounds)) {
  console.log(`${player}: ${total}`);
}
```

```text
Game Night Scoreboard: Saturday, November 14
Priya: 22
Maya: 20
Jordan: 19
```

The `10` is November because JavaScript counts months from 0. `"EEEE, MMMM d"` is date-fns's pattern for a weekday, a month and a day, written out. A path that starts with `./` is a file of yours; a bare name like `"date-fns"` is a package in `node_modules`.

## Dependencies and devDependencies

A bakery buys two kinds of things: flour and butter, which go into the bread you take home, and mixers and ovens, which never leave the kitchen. A project's packages split the same way.

- A **dependency** is a package your program needs when it runs, like `date-fns`. `npm install name` puts it under `"dependencies"`.
- A **devDependency** (a *development dependency*) is a tool you use only while you build and check the code, such as a code formatter or a test runner. `npm install --save-dev name` puts it under `"devDependencies"`. (`--save-dev` is read "dash dash save dev"; `-D` is its short form.)

Here's Prettier, the code formatter you'll set up in *Clean Code with ESLint and Prettier*, installed as a devDependency. The `@3.9.10` after its name (read "at 3.9.10") asks for that version:

```bash
npm install --save-dev prettier@3.9.10
```

```json
  "dependencies": {
    "date-fns": "^4.4.0"
  },
  "devDependencies": {
    "prettier": "^3.9.10"
  }
```

**Version numbers** follow a pattern called **semantic versioning** (semver for short): `major.minor.patch`. A *patch* (4.4.**1**) fixes bugs, a *minor* version (4.**5**.0) adds features without breaking anything, and a *major* version (**5**.0.0) may change how things work, so code written for 4 might need changes. The `^` in front, read "caret", means "this version or any newer one with the same major number": `^4.4.0` accepts 4.4.1 and 4.9.0, but not 5.0.0. To pin an exact version with no caret, add `--save-exact`.

## node_modules and package-lock.json

Open `node_modules` and you'll find a folder for each package: `date-fns` alone is over a thousand files. A real project's `node_modules` often holds tens of thousands of files and hundreds of megabytes.

Most developers keep their projects in **Git**, a tool that saves a history of a project's versions so it can be shared, on GitHub for example. Saving a version is called a **commit**. You don't need Git for this course, but you'll find its files in every project you download, so it helps to know the rule: you never write in `node_modules`, and **you never commit it**. Anyone can rebuild it in seconds from `package.json`. A file named `.gitignore` lists what Git should leave alone, and every JavaScript project's has this line:

```text
node_modules/
```

**`package-lock.json`** is different. It records the exact version of every package that was installed (`date-fns` 4.4.0, not `^4.4.0`), including the packages your packages need. Commit it, so everyone who works on the project gets the same versions you tested with. Never edit it by hand: npm keeps it up to date.

This is what makes `npm install` the first command for a project you download. With no package name, it reads `package.json` and `package-lock.json` and rebuilds `node_modules`. Delete `node_modules`, run it, and everything comes back:

```bash
npm install
```

```text
added 2 packages, and audited 3 packages in 5s

2 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities
```

**On Windows**, if the terminal says *npm.ps1 cannot be loaded because running scripts is disabled on this system*, PowerShell is blocking npm's start-up script. Run `Set-ExecutionPolicy -Scope CurrentUser RemoteSigned` once, answer `Y`, and open a new terminal.

## npm scripts

Every project has commands you run again and again: start the program, run the tests, build the site. Instead of remembering each one, a project keeps them in `package.json`, like speed-dial buttons on a phone. Each one is an **npm script**: a name, and the command it runs. Replace the `"scripts"` section that `npm init` wrote with these two:

```json
  "scripts": {
    "start": "node main.js",
    "test": "node --test"
  },
```

`npm run` and a script's name runs it. `start` and `test` are used so often that `npm start` and `npm test` work without the word `run`. npm prints the script's name and its command, then the command's output:

```bash
npm start
```

```text
> game-night@1.0.0 start
> node main.js

Game Night Scoreboard: Saturday, November 14
Priya: 22
Maya: 20
Jordan: 19
```

Any other name needs `run`: a script named `lint` runs with `npm run lint`, never `npm lint`. `npm run` on its own lists the project's scripts. The script names are the same in almost every project, so on a team, or in a project you download, `npm start`, `npm test` and `npm run build` are often all you need to know to get going.

**Try it.** Add a script named `scores` that runs `node main.js`, and run it. Then change Jordan's Charades score to 12 in `scores.js` and run it again: who's winning now?

## Check yourself

2 questions to try on the lesson page.

## So what

Every JavaScript project you'll download, join or start begins with `package.json` and `npm install`. Now you can read one, add packages to it, and run its scripts like a professional.

## Key terms

- [npm](https://www.ask-codey.com/glossary/npm)
- [Dependency](https://www.ask-codey.com/glossary/dependency-javascript)
- [Development Dependency](https://www.ask-codey.com/glossary/development-dependency)
- node_modules
- package-lock.json
- npm scripts

## Cheat sheets

- [npm and Packages](https://www.ask-codey.com/cheat-sheets/javascript-tooling-npm-and-packages-cheat-sheet)

## Related

- [Modules: import and export](https://www.ask-codey.com/lessons/javascript-modules-import-and-export)
- [A Dev Server and Build with Vite](https://www.ask-codey.com/lessons/javascript-tooling-vite)

Source: https://www.ask-codey.com/lessons/javascript-tooling-npm-and-packages
