# npm-check-updates: how to run ncu and update package.json safely

> npm-check-updates rewrites the version ranges in package.json to the latest releases, ignoring the ranges you already wrote. It solves the npm update blind spot, and it can break a build in one command.

**raineorshine/npm-check-updates** — Find newer versions of package dependencies than what your package.json allows

- Repository: https://github.com/raineorshine/npm-check-updates
- Stars: 10,318 · Forks: 372
- Language: TypeScript
- License: Apache-2.0
- Published: 2026-09-21 · Updated: 2026-09-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/raineorshine-npm-check-updates

## The gap between npm outdated and npm update

npm update respects the ranges already written in package.json. If a dependency is pinned as ^18.3.1, npm update will not cross into 19.x, no matter how long the project sits untouched. The README frames the tool's purpose in one line: it upgrades your package.json dependencies to the latest versions, ignoring specified versions. That is the whole point. It answers the question npm update cannot, which is what is actually available beyond the ceiling I set months ago.

The audience is anyone who owns a package.json with a version ceiling they no longer remember choosing. Application maintainers who want a visible diff before installing. Library authors who need to know whether their declared ranges still resolve. Monorepo maintainers, since the tool works on a package file rather than an installed tree. It is a version-range editor, not a package manager, and the README is explicit that it only modifies package.json: you run npm install afterwards to update installed packages and package-lock.json. Anyone expecting one command to do both will be surprised.

## How ncu decides which version to write

The mechanism is textual on the output side and registry-driven on the input side. ncu reads package.json, queries the registry for each direct dependency, picks a target version, then rewrites the range while preserving the operator. The README documents that policy with examples: ^1.2.0 becomes ^2.0.0, 1.x becomes 2.x, and >0.2.0 becomes >0.3.0. A less-than range is not preserved at all. <2.0.0 is replaced with ^3.0.0, and 1.0.0 < 2.0.0 also becomes ^3.0.0, because a ceiling expressed as a comparison has no equivalent in the target range. That is a real semantic change, not a cosmetic one.

The default target is the latest stable version, prereleases are ignored unless you pass --pre, and a bare * stays *. The --target flag narrows the ambition. With --target semver the tool updates inside your declared range, so ^1.1.0 becomes ^1.9.99, and an explicit upper bound survives: ^9.5.0 <10 becomes ^9.7.0 <10. With --target minor it moves patch and minor versions including major version zero, so 0.1.0 becomes 0.2.1. With --target patch, 0.1.0 becomes 0.1.2. With --target @next it takes whatever is published on the next dist-tag, so 0.1.0 becomes 0.1.1-next.1. Those four targets cover very different risk appetites, and the default is the most aggressive of them.

## Installing ncu and a first upgrade

The README gives two install paths. A global install gives you both the long name and the short ncu binary, since the package.json bin map declares npm-check-updates and ncu pointing at build/cli.js.

```bash
npm install -g npm-check-updates
```

The README also notes that npx works but only in the long form, so npx npm-check-updates is supported and the short alias is not. Requirements are stated as Node.js ^22.22.2 || ^24.15.0 || >=26.0.0 and npm >=10.0.0, so an older Node will fail before any dependency is checked.

Running ncu with no arguments prints a table of current versus latest versions and changes nothing. The README's own example output ends with the line Run ncu -u to upgrade package.json. That dry run is the safe first use, and it is also the point at which you find out whether the tool thinks your project is far behind.

```bash
ncu
```

To actually rewrite the file, add -u. The README warns in bold that this will overwrite your package file and tells you to make sure it is in version control with all changes committed.

```bash
ncu -u
npm install
```

The first command edits package.json only. The second is what updates node_modules and package-lock.json. If you skip the second, your manifest and your installed tree disagree, which is a confusing state to debug later.

## Interactive mode, filters and monorepo use

Because a blanket upgrade is rarely what you want, ncu -i opens a selection UI. The README lists the keys: arrow keys move, space toggles, a toggles all, enter upgrades. Pre-selection is where the defaults get interesting. With the default group formatting, patch and minor upgrades are pre-selected and majors are not. Disable grouping with --format no-group and everything is pre-selected, majors included. The --interactiveSelect flag makes that explicit with none, patch, minor, all, or auto.

Major version zero is treated as risky on purpose. The README states that 0.1.0 to 0.2.0 is only pre-selected by all, since anything may change before 1.0.0. Custom groups returned by --groupFunction get the same treatment. That is a considered default rather than an oversight.

Filtering works through --filter, -f, or bare arguments. ncu mocha, ncu -f mocha and ncu --filter mocha are equivalent. Multiple names work space- or comma-delimited, and wildcards or regex are accepted: ncu react-* or ncu "/^react-.*$/". Exclusion uses --reject, -x, or a leading exclamation mark, with ncu \!nodemon and ncu -x nodemon doing the same thing. The README notes that the negative-lookahead regex form needs different quoting on macOS and Linux than on Windows.

For a monorepo, the practical pattern is to run ncu per package file, or to use the filter flags to stage the work package by package. The README does not document a workspace-aware mode that walks every package.json in a workspace, so treat multi-package upgrades as a loop you write yourself rather than a feature you configure.

## Where ncu is the wrong tool

The first failure mode is the destructive one. ncu -u overwrites package.json. There is no documented rollback, no backup file, and no dry-run diff beyond the table that ncu prints before you add -u. The README's mitigation is version control, which means an uncommitted working tree is the actual hazard. If you run ncu -u with unrelated edits in package.json, you have merged two changes into one diff.

The second is the less-than rewrite. Turning <2.0.0 into ^3.0.0 is not a version bump, it is a change of intent, and it can silently admit a major line the original author deliberately excluded. If your project depends on that ceiling, the default target is the wrong one and --target semver is the flag that respects it.

The third is scope. ncu reads direct dependencies from the package file. It does not audit for vulnerabilities, it does not install anything, and it does not touch transitive versions in a lockfile. If your goal is patching a vulnerable transitive dependency, this is the wrong tool and the wrong mental model: you want a lockfile-level operation, not a manifest rewrite. The README documents none of that, and it should not, because it is out of scope by design.

## npm-check-updates compared with npm outdated and npm update

The nearest alternatives are the built-in commands, and the difference is the definition of latest. npm outdated reports what is installed versus what the registry offers, but it is a report. npm update acts on installed packages and stays inside your declared ranges. Neither will move a caret range from 18.x to 19.x in package.json.

ncu inverts the order. It edits the manifest first and leaves installation to you, which is why the README repeats that you run npm install afterwards. That inversion is the feature. You get a reviewable, commit-sized diff of version ranges, and you can reject it wholesale with git checkout. With npm update, the installed tree changes as the primary effect and the manifest is secondary. For a team that reviews changes through pull requests, the manifest-first ordering is easier to reason about.

The cost is that ncu has no opinion about whether the new version actually works. It resolves versions, not compatibility. A tool that edits ranges and a tool that installs packages are solving different halves of the same problem, and ncu deliberately only does the first half.

## Maintenance, licence and upgrade cost

The repository is not archived, and the last push was on 2026-09-18. Releases are frequent: v23.1.0 on 2026-08-23, v23.0.2 on 2026-08-07, v23.0.1 on 2026-08-02. The project is on a major-version cadence, and a tool that reads other people's manifests has to track registry and package-manager behaviour, so that cadence is not surprising.

The cost of following it is the engines field. The package declares node ^22.22.2 || ^24.15.0 || >=26.0.0 and npm >=10.0.0, and it is pure ESM with CommonJS interop through require(). If your CI image is on an older Node, a global install will not run, and the fix is an image bump rather than a flag. The Dockerfile in the repository is a one-line wrapper: it starts from node:lts-alpine, runs npm install -g npm-check-updates, and sets the entrypoint to npm-check-updates. That is the cheapest way to pin a version in CI without touching the host Node.

The licence is Apache-2.0. That is a permissive licence with an explicit patent grant and a requirement to preserve notices, but it is not legal advice and your organisation's policy on bundled dev tooling is the thing to check, not this article.

## Conclusion

Adopt npm-check-updates if you maintain a package.json whose ranges have drifted and you want a reviewable diff before npm install runs. Skip it if you cannot commit package.json first, or if you expect it to update installed packages: the README states it only modifies package.json. Before relying on it, verify your Node and npm versions against the engines field (node ^22.22.2 || ^24.15.0 || >=26.0.0, npm >=10.0.0) and confirm your registry serves the versions ncu reports.

## FAQ

### How to install npm-check-updates globally?

The README gives npm install -g npm-check-updates, which provides both the npm-check-updates and ncu commands. It also supports npx npm-check-updates, but only in the long form.

### How to use npm-check-updates?

Run ncu to print the current and latest versions without changing anything, then run ncu -u to rewrite package.json, then npm install to update installed packages and package-lock.json. The README recommends committing your package file first because -u overwrites it.

### What is npm-check-updates?

It is a CLI and module that upgrades package.json dependencies to the latest versions while ignoring the ranges you specified, preserving the range operators. It only modifies package.json, and the README notes it is compatible with npm, yarn, pnpm, deno and bun.

### How to run npm-check-updates?

Install it globally or invoke it with npx npm-check-updates, then run ncu for a read-only report or ncu -u to write the new ranges into package.json. The README notes that only the long form works with npx.

## Sources

- [Issues](https://github.com/raineorshine/npm-check-updates/issues)
- [License: Apache-2.0](https://github.com/raineorshine/npm-check-updates/blob/main/LICENSE)
- [raineorshine/npm-check-updates on GitHub](https://github.com/raineorshine/npm-check-updates)
- [README](https://github.com/raineorshine/npm-check-updates/blob/main/README.md)
- [Releases](https://github.com/raineorshine/npm-check-updates/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/raineorshine-npm-check-updates
