You’ll need Node.js 20+ and pnpm when you’re working on any project built using modern JavaScript frameworks (like Next.js, Astro, or Vite), especially if:
- The project has dependencies that need Node 20+ features (like newer APIs, performance improvements, or security stuff)
- You want faster, isolated dependency installs (pnpm is way faster and smarter than npm or yarn)
- You’re using workspaces or monorepos—pnpm handles those cleanly
- You’re contributing to open source projects that already use pnpm and expect Node 20+
Ever played a game on a phone?
- Node.js is like the phone itself—it lets you play the game.
- pnpm is the app store—it helps you find and install the game.
But here’s the thing, some games need a newer phone to work. That’s why you need Node.js version 20 or higher.
And pnpm is a newer, faster app store that doesn’t keep downloading the same game files over and over. It saves space and helps you get games quicker.
- Node.js = the thing that runs the game
- pnpm = the thing that grabs the game for you
npm
Stands for: Node Package Manager
What it is: The original tool that lets you install JavaScript packages from the npm registry (which is just a giant collection of code people share).
When you run npm install package-name
, it grabs the code from that registry and sets it up in your project.
pnpm
Stands for: Performant Node Package Manager
What it is: A newer, faster version of npm
that does the same job but smarter. It avoids copying the same files over and over.
When you run pnpm install
, it still grabs packages from the same npm registry, but it handles them more efficiently.
What pnpm can grab:
- Anything published on npm (that huge public registry of JavaScript packages).
The following are packages frequently grabbed by pnpm, grouped by the kinds of projects they typically show up in.
Web App Backend API Fullstack Appreact
express
axios
react-dom
cors
next
next
body-parser
prisma
vue
dotenv
bcrypt
svelte
mongoose
cookie-parser
vite
jsonwebtoken
react-query
tailwindcss
zod
nodemailer
CLI Tools Testing / QA Static Site / Blogyargs
jest
astro
commander
vitest
vitepress
chalk
supertest
markdown-it
ora
msw
rehype
execa
playwright
remark
figlet
cypress
@astrojs/mdx
inquirer
@testing-library/react
shiki
AI / Chatbot / MLopenai
langchain
whisper-cpp
@huggingface/inference
@xenova/transformers
pinecone-client
node-fetch
- Private packages from private registries (if you give it the right access tokens).
This is the part where things get quiet in tutorials.
When you point pnpm
at a private registry and hand it the right token, it can grab any package hosted there.
🔐 Private registries:
These are some major ones you’d run into or set up:
- GitHub Packages – popular for org-specific tools and microservices
- GitLab Package Registry – similar, mostly devops-heavy
- Verdaccio – self-hosted, light, used by smaller teams
- Artifactory (JFrog) – used by large companies for internal tools + binaries
- npm Enterprise – full private version of npm
You can configure pnpm to use any of these by updating .npmrc with auth tokens and the registry URL.
🧱 Internal Tooling (CLI, dev workflows, deploy scripts)
Internal tooling is building software with tools you don’t ship to users—they’re just for developers on your team to make the process smoother or faster.
They’re specific to how your team works. Other people wouldn’t care or couldn’t use them.
Used for:
- running tests
- setting up environments
- deploying apps
- formatting code
- checking for mistakes
Example private packages:
- @company/devops-scripts
- @company/lint-rules
- @company/ci-cli-utils
- @company/infra-monitor
- @company/git-hooks
- @company/env-checker
- @company/cloud-config
🏢 Enterprise UI Libraries
These are design and layout pieces that help your company’s apps all look the same. Like buttons, headers, or charts that follow your company’s look and feel.
They match your company’s branding—colors, styles, layout rules. No one outside your team would need those exact things.
Used for:
- creating internal dashboards
- making admin tools
- standardizing app designs across teams
Examples of private packages:
- @acme/ui-button
- @acme/theme-core
- @acme/design-system
- @acme/chart-widgets
- @acme/admin-layouts
- @acme/form-builder
- @acme/react-widgets
🔌 Microservices or SDKs for internal APIs
These are code packages that help your apps talk to your company’s private services, like internal databases or payment systems.
The services themselves are private, so the packages that wrap around them are private too.
Used for:
- logging into your company’s app
- sending internal notifications
- connecting to private APIs
Examples of private packages:
- @company/auth-client
- @company/billing-sdk
- @company/employee-api-wrapper
- @company/internal-notifications
- @company/internal-db-access
- @company/session-manager
- @company/config-provider
🧠 AI/ML + Data Tools (in-house use only)
These are packages that deal with machine learning models or data processing tools your team built for its own use.
The models or data are custom to your team. They may include confidential info or just aren’t ready for public use.
Used for:
- analyzing private datasets
- formatting internal data
- automating in-house AI tasks
Examples of private packages:
- @lab/ml-transformers
- @lab/data-cleaning-utils
- @lab/private-embedding-sdk
- @lab/model-monitor
- @lab/prompt-scripts
- @lab/vision-labeler
- @lab/whisper-runner
🔒 Security & Access Control
These are packages that help control who can do what inside your app, and keep private stuff safe. It’s all about protecting things and setting limits.
Why private:
Security stuff should never be public. Plus, your team probably has its own way of doing things that wouldn’t make sense to anyone else.
Used for:
- recording important events for auditing or safety
- checking if a user has the right role or permission
- creating or validating login tokens
Example private packages:
- @security/permissions-matrix
- @security/role-check
- @security/jwt-policy-manager
- @security/secret-injector
- @security/crypto-ops
- @security/log-auditor
- @security/vault-wrapper
📦 Mono-repo internal shared logic
These are small packages that hold pieces of code your team uses in lots of different apps inside the same codebase. They aren’t full apps—just little chunks of code you don’t want to repeat.
Why private:
They’re meant to be used by your team’s projects only. Other people wouldn’t know how to use them or wouldn’t have the rest of the setup they depend on.
Used for:
- storing constants your whole team uses
- sharing helper functions that handle common tasks
- organizing types or error messages
- keeping validation rules in one place
- centralizing analytics or tracking logic
- building a base for other tools to use
- helping keep everything consistent between apps
When companies use monorepos, they often split logic into installable chunks. These packages are not published to public npm but live in a private registry.
Example:
- @core/validation
- @core/types
- @core/notifications
- @core/analytics
- @core/cache-layer
- @core/errors
- @core/constants
OTHER PACKAGE REQUIREMENTS FOR PNPM INSTALLATION
- Local packages inside your computer (if you link them correctly).
- GitHub repos (with the right URL).
- Tarballs (a fancy word for zipped-up packages).
pnpm grabs what npm can grab, but handles storage way better.
What pnpm can’t grab?
- Anything not properly published to a registry or GitHub.
- Random JS files from websites. It needs packages, not scraps.
- Stuff that requires build tools your system doesn’t have (for example, if the package needs Python or C++ to compile parts of it, and you don’t have those tools installed, it’ll fail—but that’s not pnpm’s fault).
How to check if pnpm can grab something
Search it
Run: bashCopyEditpnpm add package-name
If it’s available, it’ll fetch it. If not, it’ll complain.
Look on npmjs.com
If a package exists there, pnpm can get it.
Check the package.json
of the project
If you see
"dependencies": { "some-package": "^1.2.3" }
, you can try installing that package directly with:
bash
pnpm add some-package
GitHub installs
You can even do:
bash
pnpm add username/repo
If something fails to install, it’s either:
- a bad package (broken or missing files)
- wrong permissions (for private packages)
- a package that needs extra system tools (and you haven’t got them)
pnpm doesn’t do deep inspection
pnpm isn’t reading the contents of the package to decide if it “makes sense” or “is useful.” It just checks the registry, the metadata in package.json
, and whether it can resolve dependencies. If those pass, it installs it—even if the package is broken, empty, malicious, or abandoned.
Isn’t that dumb? Sure. But that’s part of its job. If something’s up on the registry and checks those boxes, it’s installable.
pnpm doesn’t vet the code itself
It doesn’t know or care if the code works with your app, or if it runs in a browser or Node, or if it’s compatible with your framework. That’s on you. If you grab a browser-only package and try to use it in a Node environment, pnpm won’t stop you. It’ll install it, and your app might just crash later.
You can trick it
You could literally publish a nonsense package like this:
json{
"name": "dancing-elephant",
"version": "1.0.0",
"main": "index.js"
}
Upload it to npm with an empty index.js
, and anyone could install it using pnpm. It’ll work—because it satisfies the structure. So the real filter isn’t pnpm—it’s the registry gatekeeping (which is very loose), and your own judgment.
Registry origin matters
pnpm defaults to registry.npmjs.org, but you can point it to private registries (like GitHub Packages, Verdaccio, Jfrog Artifactory). If you do, that registry becomes the rule-maker. pnpm just plays middleman.
Some packages require extra setup
Some grab just fine, but don’t work without:
- Post-install scripts
- Native binaries
- Specific Node versions
- Peer dependencies you didn’t install yet
pnpm will still fetch them, but they won’t run until you fix those things
pnpm will grab a package if
✅ 1. It’s published on npm (the registry)
The package has to be available on the npm registry. Doesn’t matter who wrote it—as long as it’s up there, pnpm can install it. That’s where it pulls from by default.
✅ 2. It has a package.json file
This file tells pnpm what the package is called, what version it is, what it depends on, and how to run it. No package.json, no install.
✅ 3. It uses a format pnpm understands (CommonJS, ESM, etc.)
As long as the code is valid JavaScript (or TypeScript that’s been compiled), and uses a module format that Node.js supports, it’s fine. pnpm just puts the files where they belong.
✅ 4. It’s versioned properly
The package has to follow some sort of versioning (like 1.0.0), so pnpm knows what version you’re asking for. That’s how it handles updates and resolves conflicts between different packages.
✅ 5. It’s not gated behind private access (unless configured)
If the package is private or behind a login (like some company’s internal packages), then you’ll need to give pnpm access (via .npmrc or auth token). Otherwise, it can’t grab it.