cheat sheet
npx
Run npm package binaries without global installs using npx. Covers resolution order, version pinning, --yes, --no-install, npm exec, scaffolders, and cache management.
npx — Execute npm Packages
What it is
npx is an npm package runner bundled with npm since v5.2 (Node 8.2+). It lets you execute package binaries without installing them globally. When you run npx some-tool, it resolves the binary in this order:
./node_modules/.bin/— the local project's installed packagesPATH— globally installed binaries- The npm registry — downloads and caches the package temporarily, then runs it
This means you can run any CLI tool from the registry without a global install and always get the version you specify.
Basic usage
# Run a package binary from the registry (downloads if needed)
npx cowsay "Hello from npx"
Output:
_________________
< Hello from npx >
-----------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
# Scaffold a new project
npx create-react-app my-app
npx create-next-app@latest my-next-app
npx create-vite my-vite-app
Output (npx create-next-app@latest my-next-app):
Need to install the following packages:
create-next-app@15.3.0
Ok to proceed? (y) y
✔ Would you like to use TypeScript? … Yes
✔ Would you like to use ESLint? … Yes
✔ Would you like to use Tailwind CSS? … Yes
✔ Would you like to use `src/` directory? … Yes
✔ Would you like to use App Router? … Yes
✔ Would you like to customize the default import alias (@/*)? … No
Creating a new Next.js app in /home/user/my-next-app.
Specifying a version
# Run an exact version of a package
npx typescript@5.4 tsc --version
Output:
Version 5.4.5
# Run with a semver range
npx eslint@^8 --version
npx prettier@3 --version
# Always use the latest (bypass cache)
npx --yes create-vite@latest my-app
Output: (none — exits 0 on success)
--package flag
Use --package when the binary name differs from the package name, or to install multiple packages for a single command:
# Install typescript first, then run tsc
npx --package=typescript -- tsc --init
# Multiple packages in one invocation
npx --package=typescript --package=ts-node -- ts-node script.ts
Output (npx --package=typescript -- tsc --init):
Created a new tsconfig.json with:
target: es2016
module: commonjs
strict: true
esModuleInterop: true
...
You can learn more at https://aka.ms/tsconfig
--yes flag (skip confirmation)
By default, npx asks for confirmation before downloading a package not already installed. Use --yes (or -y) to skip that prompt in scripts and CI:
npx --yes create-next-app@latest my-app
npx -y prettier@3 --write .
Output: (none — exits 0 on success)
npm exec vs npx
Since npm 7, npm exec and npx are functionally identical. The npm exec form is more explicit and preferred in npm scripts:
# These are equivalent
npx tsc --version
npm exec -- tsc --version
npm exec --package=typescript -- tsc --version
Output: (none — exits 0 on success)
In package.json scripts, prefer npm exec over npx:
{
"scripts": {
"typecheck": "npm exec -- tsc --noEmit",
"scaffold": "npm exec --yes --package=plop -- plop"
}
}
--no-install flag
Fail immediately if the binary is not already installed locally — useful in CI to prevent surprise downloads:
npx --no-install prettier --version
Output (if not installed):
npm error could not determine executable to run
Output (if installed locally):
3.2.5
Resolution order in detail
When you run npx some-tool, resolution happens in this exact order:
1. ./node_modules/.bin/some-tool ← local node_modules (fastest, preferred)
2. $PATH (globally installed binaries)
3. npm registry download → ~/.npm/_npx/<hash>/node_modules/.bin/some-tool
This means:
- If you have a local install (
npm install --save-dev prettier),npx prettieruses it. - If
prettieris not local but is in your$PATH(global install), that runs. - Otherwise, npx downloads it from the registry, caches it, and runs it.
Running local binaries
The three equivalent ways to run a locally installed binary:
# 1. npx (recommended — readable, handles PATH automatically)
npx tsc --version
# 2. Direct path (verbose but unambiguous)
./node_modules/.bin/tsc --version
# 3. npm run (requires a script entry in package.json)
npm run typecheck
Output: (none — exits 0 on success)
# npx with a locally installed tool — shows which binary is used
npx --no-install prettier --write src/
Output:
src/index.ts 89ms
src/utils.ts 12ms
src/components/Button.tsx 34ms
Common use cases
Scaffolders — creating new projects
# React
npx create-react-app my-app
npx create-react-app my-app --template typescript
# Next.js
npx create-next-app@latest my-next-app
# Vite
npx create-vite my-vite-app
npx create-vite my-vite-app --template react-ts
# Astro
npx create-astro my-astro-site
# Clone a GitHub template without git history (degit)
npx degit user/repo my-app
npx degit github:user/repo#branch my-app
# Download a template (giget — supports GitHub, GitLab, Bitbucket)
npx giget gh:user/repo my-app
Output: (none — exits 0 on success)
One-off formatting and linting
# Format all files without a local install
npx prettier@3 --write .
# Lint with ESLint
npx eslint src/
# Run a TypeScript type check
npx typescript@5 tsc --noEmit
# Convert a project to ESM
npx esm-to-esm src/
Output: (none — exits 0 on success)
Code generation and migration tools
# Generate a component with plop
npx plop component MyButton
# Run codemods (e.g., React 18 migration)
npx react-codemod rename-unsafe-lifecycles src/
# OpenAPI client generation
npx @openapitools/openapi-generator-cli generate \
-i openapi.yaml -g typescript-fetch -o src/generated
Output: (none — exits 0 on success)
Inspecting packages without installing
# Check what files a package contains before installing
npx npm-check-updates
# Visualize your dependency tree
npx npmgraph .
# Find outdated or duplicate packages
npx depcheck
Output: (none — exits 0 on success)
Cache management
npx caches downloaded packages in the npm cache directory so repeat invocations are fast:
# Show where the npm cache lives
npm config get cache
Output:
/home/user/.npm
# See how much cache space is used
du -sh ~/.npm/_npx/
Output:
148M /home/user/.npm/_npx/
# Clear the entire npm cache (including npx downloads)
npm cache clean --force
Output:
npm warn using --force Recommended protections disabled.
# Verify the cache is intact (without cleaning)
npm cache verify
Output:
Cache verified and compressed (~/.npm):
Content verified: 1432 (61.2MB)
Index entries: 2011
Finished in 4.231s
Comparison: npx vs npm run vs direct path
| Method | Requires package.json entry | Uses local install | Downloads from registry | Example |
|---|---|---|---|---|
npx cmd | No | Yes (prefers) | Yes (if needed) | npx prettier --write . |
npm run cmd | Yes (script entry) | Yes | No | npm run format |
./node_modules/.bin/cmd | No | Yes | No | ./node_modules/.bin/prettier . |
npm exec -- cmd | No | Yes (prefers) | Yes (if needed) | npm exec -- prettier . |
In CI and scripts that should be reproducible, always prefer
npm runwith a script defined inpackage.json, ornpx --no-installto prevent accidental registry downloads. Reserve plainnpx <tool>for interactive developer use.
Running
npxwith an unrecognized package name downloads and executes arbitrary code. Always verify the package name matches the official package before running — typosquatting attacks exist on the npm registry.
Resolution algorithm — deep-dive
The full resolution sequence npx uses when you invoke npx some-tool. Understanding this matters when a tool runs the wrong version, fails silently, or pulls from an unexpected registry. Reach for this section when debugging "why did npx run the wrong thing?".
1. Parse the invocation
↓
npx <flags> <pkg-spec> -- <args>
↓
Strip flags; extract pkg-spec.
↓
2. Resolve binary name
↓
- If pkg-spec contains '/', '@', or starts with '.', treat as package
- Otherwise treat as binary name
↓
3. Check local node_modules (./node_modules/.bin/)
↓ Hit? → run it. Done.
↓ Miss?
4. Check ancestor node_modules (walk up to /)
↓ Hit? → run it. Done.
↓ Miss?
5. Check $PATH (system PATH, including nvm/global installs)
↓ Hit? → run it. Done.
↓ Miss?
6. Check npm cache (~/.npm/_npx/<hash>)
↓ Hit? → run cached version. Done.
↓ Miss?
7. Download from the registry into ~/.npm/_npx/<hash>
↓ Prompt for confirmation (skip with --yes)
↓ Install (respects .npmrc, scopes, auth)
↓ Run the binary from the temp install.
Verify which path npx takes:
# Trace npm internals (very verbose)
NODE_DEBUG=cli npx --loglevel=verbose prettier --version 2>&1 | head -30
Output:
npm verbose cli /usr/lib/node_modules/npm/bin/npx-cli.js
npm verbose exec packages: ["prettier"]
npm verbose exec args: ["--version"]
npm http fetch GET 200 https://registry.npmjs.org/prettier 18ms
3.3.3
Hash-based cache directory
Each invocation gets a stable cache directory keyed by an MD5 of the npx package spec. Repeat invocations are instant after the first.
ls ~/.npm/_npx | head
Output:
1a2b3c4d5e6f7890
2b3c4d5e6f789012
3c4d5e6f78901234
ls ~/.npm/_npx/1a2b3c4d5e6f7890/node_modules/
Output:
.bin/
.package-lock.json
prettier/
If a cache entry corrupts, delete the specific subdirectory rather than the whole _npx/:
rm -rf ~/.npm/_npx/1a2b3c4d5e6f7890
Output: (none — exits 0 on success)
Difference between local resolution and npm exec
Plain npx tool does resolution 1→7. npm exec --no-install -- tool stops at step 5. npm exec --package=tool -- tool skips steps 1→5 and goes straight to step 6/7.
# Use only the locally-installed eslint (fast, deterministic)
npm exec --no-install -- eslint --version
# Force a fresh download, ignore any local install
npm exec --yes --package=eslint@latest -- eslint --version
Output: (none — exits 0 on success)
-c / --call — chain multiple commands
--call (or -c) runs an arbitrary shell-style command inside a context where the installed binary is on PATH. Useful for one-line chains where the package provides multiple binaries.
# Install create-vite + run a follow-up
npx -c 'create-vite my-app --template vue && cd my-app && npm install'
# Run several binaries from a single package
npx --package=typescript -c 'tsc --version && tsserver --version'
Output:
Version 5.5.4
TypeScript Server 5.5.4
The shell that interprets -c is the one in $SHELL (or cmd.exe on Windows). For cross-platform robustness, prefer chaining at the npm script level.
Multiple packages with --package
--package (alias -p) installs additional packages into the temporary environment before running the binary. Useful when:
- The binary name differs from the package name (e.g.
create-vitebinary lives in packagecreate-vite, buttsclives intypescript). - One command needs binaries from multiple packages (e.g.
ts-node+typescript).
# Install typescript first, run tsc from it
npx --package=typescript -- tsc --init
# Multiple packages — install both, then run ts-node
npx --package=typescript --package=ts-node -- ts-node script.ts
# Pin versions on each
npx --package=typescript@5.5 --package=ts-node@10.9 -- ts-node script.ts
Output (npx --package=typescript -- tsc --init):
Created a new tsconfig.json with:
target: es2016
module: commonjs
strict: true
esModuleInterop: true
You can learn more at https://aka.ms/tsconfig
Specifying a registry per-invocation
When a tool lives on a private registry, pass --registry or use .npmrc. The flag wins over .npmrc.
# One-off from GitHub Packages
npx --registry=https://npm.pkg.github.com @my-org/codegen build
# From a corporate proxy
npx --registry=https://npm.corp.example.com prettier --check .
Output: (none — exits 0 on success)
For long-term setup, put it in .npmrc:
# .npmrc
@my-org:registry=https://npm.pkg.github.com/
//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}
Comparison: npx vs pnpm dlx vs yarn dlx vs bunx
All four are "run a CLI from a package without installing", but they cache differently and have different defaults.
| Feature | npx | pnpm dlx | yarn dlx | bunx |
|---|---|---|---|---|
| Confirmation prompt | Yes (skip with --yes) | No | No | No |
Reads local node_modules | Yes | No (forces fresh) | No (forces fresh) | Yes |
| Cache location | ~/.npm/_npx/<hash> | pnpm store | Yarn cache | Bun store |
| Cache hit speed | Fast | Faster (CAS links) | Fast (zip) | Fastest |
| Sandboxing | None | None | None | None |
| Cold install of a 40-pkg CLI | ~8 s | ~6 s | ~5 s | ~1.5 s |
--package for multi-pkg env | Yes | Yes (--package) | No (use shell) | No |
Works without a package.json | Yes | Yes | Yes | Yes |
| Default version | latest from registry | latest | latest | latest |
# Same task, four tools
npx create-vite my-app
pnpm dlx create-vite my-app
yarn dlx create-vite my-app
bunx create-vite my-app
Output: (none — exits 0 on success)
pnpm dlxis the strictest of the four — it never falls back to a locally-installed copy and always pulls a fresh resolution. Use it when reproducibility outweighs speed (e.g. running a scaffolder that must not pick up an old local install).
See pnpm, yarn, and bun for the host tools.
Security considerations
npx downloads and executes code from the network. Treat every invocation as curl | sh of code you didn't write.
Typosquatting and dependency confusion
- Typo:
npx loadsh(instead oflodash) can fetch malicious code if a squatter has registered the typo. - Dependency confusion: if your private
@my-org/fooexists publicly under the same name,npx @my-org/foomay resolve to the public (malicious) version.
Defences:
# Always pin a version
npx prettier@3.3.3 --check .
# Use --package to be explicit about the package, not the binary name
npx --package=prettier@3.3.3 -- prettier --check .
# Inspect before running
npm view prettier@3.3.3
Output (npm view prettier@3.3.3):
prettier@3.3.3 | MIT | deps: none | versions: 158
Prettier is an opinionated code formatter
https://prettier.io
keywords: format, prettier
dist
.tarball: https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz
.shasum: 7c54fd35e9d8e21ec1b16f1c75f5db59c1c4f17e
.integrity: sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==
--ignore-scripts
Some packages run install scripts that touch the filesystem. Disable them when you're just trying to execute a binary:
npx --ignore-scripts prettier@3.3.3 --check .
Output: (none — exits 0 on success)
Pinning in CI
CI scripts that use plain npx <tool> are non-deterministic — the package can update between runs. Pin or vendor:
# .github/workflows/ci.yml — pin npx tool versions
- run: npx --yes prettier@3.3.3 --check .
- run: npx --yes typescript@5.5.4 tsc --noEmit
npx --yes prettier@3.3.3 --check .
Output: (none — exits 0 on success)
npx without npm — alternatives
If you don't have npm installed, several drop-in alternatives can run packages from the npm registry.
| Tool | Provided by | Notes |
|---|---|---|
npx | npm 5.2+ | Default; everywhere Node is installed |
pnpm dlx | pnpm | Faster cold start; strict (no local lookup) |
yarn dlx | Yarn v2+ | Zip-cached; Berry-only |
bunx | Bun | Fastest cold start |
deno run | Deno | Direct from URL (deno run npm:prettier) |
# Run prettier from Deno without a Node install
deno run --allow-read --allow-write npm:prettier@3.3.3 --check .
Output: (none — exits 0 on success)
See deno for the npm: specifier system.
Common pitfalls
npxrunning cached old version — when you don't pin (npx tool), npx prefers cached over latest. Pass--ignore-existingor usenpx tool@latestto bypass the cache.npx <package>hanging on prompt in CI — interactive shells get the "Ok to proceed?" prompt. Always pass--yesin non-interactive contexts.- Local install silently used —
npx toolprefers./node_modules/.bin/toolover the registry. To force a fresh install, use--package=tool@latestorpnpm dlx tool. npx <binary>saying "could not determine executable" — the binary name doesn't match the package name. Use--package=<pkg> -- <binary>. Example:npx --package=@org/cli -- cli build.npxfailing on Windows with paths containing spaces — quote the whole invocation and use forward slashes. Alternatively run from PowerShell which handles spaces better thancmd.exe.- Auth tokens leaking to the cache —
~/.npm/_npx/contents are world-readable by default. Setumask 077for the npm cache or usecache=~/.npm-privatewith restrictive perms. npxnot finding local binaries — happens whennpm installwas interrupted. Recreatenode_modules/.bin:rm -rf node_modules && npm install.- Different behaviour in
npm scriptsvs interactive shell — npm scripts injectnode_modules/.bininto PATH, so plainprettierworks withoutnpx. Outside scripts, you neednpx prettieror the full path.
Real-world recipes
One-line project scaffold
The most common use of npx — bootstrap a new project from a template with no prior install.
npx --yes create-next-app@latest my-app \
--typescript --tailwind --eslint --app --no-src-dir --import-alias '@/*'
Output:
Creating a new Next.js app in /home/alice/my-app.
Installing dependencies:
- react
- react-dom
- next
added 367 packages in 14s
Success! Created my-app at /home/alice/my-app
Compare two versions of a tool
Useful when debugging a regression — run the same input against two versions side by side.
npx prettier@3.2.5 --version
npx prettier@3.3.3 --version
diff <(npx prettier@3.2.5 --check src/) <(npx prettier@3.3.3 --check src/)
Output:
3.2.5
3.3.3
Run a one-shot codemod
Codemods rewrite source files at scale. jscodeshift powers most of them and is best run via npx so the version matches the codemod's expectations.
npx --yes jscodeshift@latest -t ./codemods/rename-prop.js src/
Output:
Processing 142 files...
All done.
Results:
0 errors
37 unmodified
105 modified
Pinned scaffolder + post-init script
Combine --package and -c for a deterministic project setup.
npx --yes -p create-vite@5.5.0 -p typescript@5.5.4 \
-c 'create-vite my-app --template react-ts && cd my-app && npm install && npm run typecheck'
Output:
Scaffolding project in /home/alice/my-app
Done. Now run:
cd my-app
npm install
npm run dev
added 220 packages in 9s
> tsc --noEmit
Bypass cache when latest matters
You always want the freshest release for security tools.
npx --yes --ignore-existing npm-audit-html@latest --output report.html
Output: (none — exits 0 on success)
Run from a GitHub repo (no registry needed)
npx accepts a github:user/repo spec — Useful for testing PRs or running unpublished tools.
npx github:alicedev/my-tool#feature-branch
Output: (none — exits 0 on success)
Sandbox a dangerous tool
When auditing an unknown package, run inside a container so it can't touch your home directory.
docker run --rm -it -v "$PWD":/work -w /work node:22-alpine \
sh -c 'npx --yes some-unknown-tool --version'
Output:
some-unknown-tool 1.0.0
Replace global installs
Global tools age badly — they accumulate, version-drift, and pollute PATH. Replace npm install -g with npx (pinned) at the call site.
# Before: npm install -g typescript prettier eslint
# After:
npx --yes typescript@5.5.4 tsc --version
npx --yes prettier@3.3.3 --check .
npx --yes eslint@9.10.0 src/
Output: (none — exits 0 on success)
Or commit the versions to package.json devDependencies and use npm exec / npm run — no npx needed.