Dependency updates. Your call.
See what's outdated, read what changed, and pick what to upgrade. Works with npm, pnpm, Yarn, and Bun, and stays fast on big monorepos (benchmarks).
Run this in your project:
npx inupRequires Node.js 22.19+. No configuration needed.
| inup | npm-check-updates | taze | |
|---|---|---|---|
| Interactive picker | ✓ | ✓ | ✓ |
| Monorepos and workspaces | ✓ | ✓ | ✓ |
| Release-age cooldown | ✓ | ✓ | ✓ |
| Each package's changelog, inside the picker | ✓ | ||
| Vulnerability audit, with the versions that fix it | ✓ | ||
| A scheduled update PR from a GitHub Action | ✓ |
Other tools' features as their READMEs describe them, October 2026.
Move with ↑ / ↓, select with Space, and press Enter to apply. Your dependency files stay untouched until you confirm. Then inup writes the selected versions and runs your package manager's install to update the lockfile.
| Want to… | Press |
|---|---|
| Find a package | / |
| Read its changelog | i |
| Check known vulnerabilities and available fixes | s |
| Select updates within your existing version ranges | m |
| Choose an in-range update or the latest version | ← / → |
Review the diff and run your tests after upgrading.
All keyboard shortcuts
| Key | Action |
|---|---|
↑ / k |
Move up |
↓ / j |
Move down |
g / Home |
Jump to the first package |
G / End |
Jump to the last package |
PgUp |
Move up one page |
PgDn |
Move down one page |
← |
Cycle selection left (latest → range → none) |
→ |
Cycle selection right (none → range → latest) |
Space |
Toggle the current package on/off |
m |
Select all minor/patch updates, except peers and patched packages |
l |
Select all latest updates (including major), except peers and patched packages |
u |
Unselect all packages |
Enter |
Confirm selection and upgrade |
/ |
Search packages by name |
d |
Toggle devDependencies |
p |
Toggle peerDependencies |
o |
Toggle optionalDependencies |
s |
Run the vulnerability audit |
v |
Show only vulnerable packages |
c |
Show packages held back by the release-age cooldown |
Esc |
Clear the active search filter |
i |
View package details and changelog |
t |
Change the color theme |
? |
Show this help |
! |
Show the performance/debug panel |
q |
Quit without changes |
inup detects your package manager and finds dependencies across workspaces. Private registries use your .npmrc. pnpm catalog entries are updated in pnpm-workspace.yaml.
Only after a few packages? Name them: npx inup react "eslint-*", or for every run with only in .inuprc. Global tools too: npx inup -g upgrades what you installed with npm install -g (or pnpm and Bun with --package-manager). Need to leave a package alone? Run npx inup --ignore "react,react-dom", or save your rules in .inuprc.
npx inup --verify "npm test"After you upgrade, inup runs your tests. If they fail, it puts everything back and tries one package at a time, so you keep every upgrade that passes and nothing that breaks. Add "verify": "npm test" to .inuprc to check every upgrade, in the picker, with --apply and in the GitHub Action.
On a first run, inup lists outdated dependencies up to 15× faster than npm-check-updates and up to 13× faster than taze on these repositories.
How long each tool takes, on public repositories pinned to a commit, to list every outdated dependency and to open its picker with every update loaded. Lower is better.
All timings
Each tool prints every outdated dependency and exits.
| First run | expressjs/express 1 package.json · 44 dependencies |
vuejs/core 22 package.json files · 69 dependencies |
TanStack/query 105 package.json files · 190 dependencies |
vendurehq/vendure 54 package.json files · 323 dependencies |
backstage/backstage 269 package.json files · 591 dependencies |
|---|---|---|---|---|---|
| inup | 0.27 s | 0.63 s | 0.70 s | 1.24 s | 1.39 s |
| npm-check-updates | 0.74 s | 2.55 s | 10.5 s | 3.75 s | 11.4 s |
| taze | 0.70 s | 1.21 s | 9.30 s | 5.22 s | 8.87 s |
| syncpack | 0.25 s | 0.83 s | 2.74 s | 3.48 s | 3.21 s |
| npm-check | 1.14 s | — | — | — | — |
| npm / pnpm / bun outdated | 0.91 s | 1.39 s | 2.83 s | 1.47 s | — |
| Repeat run | expressjs/express | vuejs/core | TanStack/query | vendurehq/vendure | backstage/backstage |
|---|---|---|---|---|---|
| inup | 0.18 s | 0.22 s | 0.37 s | 0.54 s | 0.93 s |
| npm-check-updates | 0.75 s | 2.63 s | 10.3 s | 3.72 s | 11.4 s |
| taze | 0.21 s | 0.38 s | 6.78 s | 1.89 s | 5.00 s |
| syncpack | 0.04 s | 0.15 s | 0.46 s | 0.64 s | 0.51 s |
| npm-check | 1.15 s | — | — | — | — |
| npm / pnpm / bun outdated | 0.69 s | 0.14 s | 3.69 s | 0.08 s | — |
Each tool's interactive list of updates, timed until every update is loaded.
| First run | expressjs/express | vuejs/core | TanStack/query | vendurehq/vendure | backstage/backstage |
|---|---|---|---|---|---|
| inup | 0.40 s | 0.57 s | 0.95 s | 1.40 s | 2.12 s |
| npm-check-updates | 0.70 s | — | — | — | — |
| taze | 1.05 s | 1.34 s | 8.84 s | 4.53 s | 8.76 s |
| syncpack | 0.27 s | 0.81 s | 2.60 s | 3.35 s | 3.24 s |
| npm-check | 1.17 s | — | — | — | — |
| pnpm / bun update -i / yarn upgrade-interactive | — | 1.37 s | 2.79 s | 1.47 s | 7.64 s |
| Repeat run | expressjs/express | vuejs/core | TanStack/query | vendurehq/vendure | backstage/backstage |
|---|---|---|---|---|---|
| inup | 0.33 s | 0.35 s | 0.64 s | 0.68 s | 1.17 s |
| npm-check-updates | 0.75 s | — | — | — | — |
| taze | 0.23 s | 0.39 s | 0.45 s | 1.88 s | 5.27 s |
| syncpack | 0.28 s | 0.77 s | 2.61 s | 3.37 s | 3.31 s |
| npm-check | 1.18 s | — | — | — | — |
| pnpm / bun update -i / yarn upgrade-interactive | — | 0.15 s | 3.48 s | 1.47 s | 2.56 s |
First run: every cache the tool keeps is emptied before each run. Repeat run: caches kept. Median of 10 runs per tool on GitHub Actions ubuntu24 20261023.334.3, 4 CPUs, Node v24.21.0, 24 Oct 2026; listing timed with hyperfine 1.20.0. Pickers run in a 160×50 terminal and count as loaded at the last thing they draw or write to their cache before 5 s of quiet, so rows fetched off screen count too. inup runs with --no-audit: the other tools don't check for vulnerabilities, so it skips its check too. inup runs on its native (Rust) core, built from the same commit. Fastest in bold.
How it's measured, tool versions and exact commands
A version published an hour ago is the one most likely to be a compromised release nobody has caught yet. Give releases a cooldown and inup stops offering anything younger — in the picker, in reports, and in --apply:
npx inup --minimum-release-age 10080 # nothing published in the last 7 daysKeep it in .inuprc as minimumReleaseAge (minutes, the same name and unit pnpm uses), with minimumReleaseAgeExclude for your own packages.
inup tells you what it held back rather than quietly showing you fewer updates, so a package waiting out its cooldown never looks the same as a package that is up to date.
Registries that don't publish release times are unaffected — the cooldown only acts on evidence it actually has.
Let inup run on a schedule. It opens a dependency-update pull request and refreshes that same PR on later runs. You review and merge when you're ready.
Add the workflow to your repo
Save this as .github/workflows/inup.yml:
name: Dependency updates
on:
schedule:
- cron: '17 5 * * 1' # Mondays at 05:17 UTC
workflow_dispatch:
permissions:
contents: write
pull-requests: write
jobs:
upgrade:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: donfear/inup@v1
with:
target: minorIn your repo's Settings → Actions → General → Workflow permissions, enable Allow GitHub Actions to create and approve pull requests.
minor stays within existing version ranges, following each range's operator: ^0.2.3 stays on 0.2.x and ~1.2.3 on 1.2.x (full rule). Use patch for patch updates only, or latest to include major upgrades.
If you need the PR to trigger CI, pass a personal access token through the action's token input. See the Action guide for all inputs and outputs.
inup fetches and parses registry data with a native core written in Rust: on large projects it uses about half the CPU and a quarter less memory, and the list stays responsive while packages load. It's on by default.
inup itself installs no native code. The first interactive run downloads the core for your platform (about 1.5 MB), checks it against the hash built into your copy of inup and caches it; from the next run on, inup uses it, re-checking the cached file each time. A core that doesn't match is never loaded, whichever registry served it. Scripted runs (--json, --check, --apply, CI) use the core once it's cached but never download it themselves, so they finish without waiting on it. Supported on macOS, Linux and Windows (x64 and arm64). If it can't be used, inup quietly falls back to the standard core.
npx inup --no-native # standard core for this run
npx inup --native # native core, downloading it even in a scripted runTo turn it off for a project, add "native": false to .inuprc.
npx inup --check # Exit 1 if updates exist; don't change files
npx inup --json # Print a JSON report; don't change files
npx inup --apply # Apply in-range updates and run install
npx inup --apply --verify "npm test" # Keep only the upgrades your tests pass withCLI reference · CI guide · Troubleshooting
No telemetry or tracking. inup contacts your package registry for metadata and security advisories, npm for download counts, and GitHub for release notes.

