Why pnpm is Faster and More Space-Efficient than npm?
November 7, 2024
If you work in frontend development, you've certainly heard of npm, probably know about yarn, and may have encountered the buzzy newcomer bun in the last two years. But there's another package manager that many development teams relied on before bun came along: pnpm.
In this article, we will explore the following questions
- How can package managers optimize both speed and disk space in large JavaScript projects?
- Why do traditional package managers like npm and yarn struggle with efficiency at scale?
- What makes pnpm’s approach to dependency management fundamentally different from other tools?
What is pnpm?
In the JavaScript ecosystem, package managers play a crucial role in managing project dependencies. While npm has been the default choice since Node.js's inception, pnpm (performant npm) emerged as a better solution.
pnpm markets itself as a "Fast, disk space efficient package manager." These aren't just marketing claims - they represent fundamental architectural improvements in how JavaScript dependencies are handled. Think of it as the difference between a library with multiple copies of the same book (npm) versus a single copy with multiple reference cards pointing to it (pnpm).
Why is pnpm faster?
Let's start with speed. pnpm's superior performance compared to npm or yarn comes from a clever architectural choice: dependency sharing.
Imagine you're installing three different packages that all depend on React. With npm, you'd download and unpack React three times. pnpm, however, says "Hey, we've already got React - let's just use that copy."
This sharing mechanism, combined with local caching, means pnpm can often complete installations even when your internet connection is slow, as it's pulling from local storage rather than downloading everything fresh.
Consider this common scenario:
Project A/
node_modules/
lodash/ (5MB)
react/ (3MB)
Project B/
node_modules/
lodash/ (5MB)
vue/ (2MB)
Traditionally, npm manages package in a way that each project maintains its own complete copy of dependencies. In addition, identical packages are duplicated across projects, meaning a single package like lodash gets stored multiple times.
However, pnpm does things differently. Below is roughly how pnpm stores packages
.pnpm-store/
v2/
files/
[hash1] -> lodash files
[hash2] -> react files
[hash3] -> vue files
As you can see, files are stored once and shared across all projects. This means for a new project, it can access to previously downloaded packages instantly, making it way faster than the npm approach.
Why is pnpm more space efficient?
The space efficiency is even more interesting. pnpm uses what's called a "global store" - think of it as a central library where all unique dependencies live exactly once.
Traditional package managers like npm and yarn are more like having a separate bookshelf in every room of your house, with duplicate copies of the same books. If ten of your projects use React 18.2.0, npm will store ten copies, while pnpm stores it once and creates lightweight references. The space savings can be enormous in large development environments.
To understand why this design is clever, we need to know how Node.js finds dependencies.
If you do the following import
import fs from 'fs'
pnpm will do the following:
- First, it looks for built-in modules (like
fs
) - If it's not built-in, it searches in
node_modules
- If nothing's found, it keeps looking up the directory tree until it either finds the module or throws an error
pnpm's innovation is in how it structures these lookups. When you peek into a pnpm-managed node_modules
directory, you'll see arrow symbols - these are symbolic links pointing to the global store (see the following).
node_modules/
package-a -> ../.pnpm/package-a@1.0.0/node_modules/package-a
It's like having a card catalog (the links) that points to a central library collection (the global store in .pnpm
). The actual files live only in this central location, with everything else being just references.
The technical implementation lives in the .pnpm
directory within node_modules
. This is where the real files are stored - everything else is just a pointer. It's a bit like how modern operating systems handle files: what looks like a file to you might actually be just a shortcut pointing to the real data somewhere else.
With this approach, pnpm uses far less space compared to npm.
Mastering data structures matters for frontend developers
What’s illustrated above is a perfect example of why data structures matter in frontend development. By reimagining how dependencies are stored and accessed, pnpm achieved significant improvements in both speed and storage efficiency. It's the difference between having a thousand copies of a book scattered across different libraries versus having one copy and a thousand library cards - same access, far less waste.
The broader lesson here isn't just about package managers - it's about how fundamental computer science concepts like efficient data structures can make even seemingly solved problems in modern web development better. Sometimes, the clever application of basic principles can lead to dramatic improvements in real-world systems.
This serves as a reminder that in frontend development, understanding core computer science concepts can lead to elegant solutions to complex problems.