If you've worked with Node.js or npm, you've definitely seen package.json and package-lock.json sitting side by side in your project. Many developers know they're related to dependencies, but not everyone clearly understands why both exist and what makes them different.
Let's break it down.
What is package.json?
package.json is the project manifest file. It describes your project and lists the dependencies your application needs.
It typically includes:
- Project name and version
- Scripts (
npm start,npm build, etc.) - Dependencies and dev dependencies
- Metadata (author, license, etc.)
Example
{
"name": "my-app",
"version": "1.0.0",
"dependencies": {
"express": "^4.18.2"
}
}
Key Point
When you write "express": "^4.18.2", the ^ means npm can install any compatible version, such as 4.18.2, 4.19.0, or any 4.x.x release.
This means installs may differ over time—which can cause problems.
What is package-lock.json?
package-lock.json is automatically generated by npm. It locks the exact versions of every installed dependency, including sub-dependencies.
It ensures:
- Same dependency tree across all environments
- Same versions for everyone on the team
- Reproducible installs across machines
Example (simplified)
{
"dependencies": {
"express": {
"version": "4.18.2",
"resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz",
"integrity": "sha512-..."
}
}
}
This file guarantees that everyone installs the exact same version, even if package.json allows flexibility.
Main Differences
| Feature | package.json | package-lock.json |
|---|---|---|
| Purpose | Defines dependencies | Locks exact versions |
| Created by | Developer | npm automatically |
| Should you edit? | Yes | No |
| Version flexibility | Yes | No |
| Included in Git? | Yes | Yes |
| Controls sub-dependencies | No | Yes |
Why Both Files Are Needed
Think of it like this:
package.json→ What you wantpackage-lock.json→ What you actually got
Example scenario:
- You install today → version 1.2.3
- Your teammate installs tomorrow → version 1.2.5
- App breaks due to subtle differences
With package-lock.json, everyone installs exactly 1.2.3—no surprises.
When Does package-lock.json Update?
It updates when:
- You run
npm install - You add or remove packages
- You run
npm update
Should You Commit package-lock.json?
Yes—always commit it to Git.
Benefits:
- Reproducible builds
- Faster installs (npm can skip resolution)
- Prevents "works on my machine" issues
Quick Analogy
package.json= Shopping listpackage-lock.json= Receipt with exact brands and quantities
Conclusion
Both files serve different but complementary purposes:
- Use package.json to declare what dependencies you need
- Use package-lock.json to guarantee everyone gets the same versions
Never delete package-lock.json unless you intentionally want to regenerate your entire dependency tree.
Tagged with
Written by
DebuggerMe TeamThe DebuggerMe team builds developer tools, writes technical content, and helps teams ship better software.
Related Articles
All articles →Node.js 22 Is Here — Everything You Need to Know
Node.js 22 lands as the new LTS with native TypeScript type-stripping, a built-in test runner improvements, WebSocket client, and the much-anticipated require(esm) support. Here's a practical breakdown.
Why TypeScript Generics Are More Powerful Than You Think
A deep dive into TypeScript's generic type system — from basic usage to advanced patterns like conditional types, infer, and mapped types that will make your code safer and more expressive.
Docker for Developers — From Zero to Production-Ready in One Guide
Docker is non-negotiable in modern development. This guide takes you from installing Docker to running a full multi-service production stack with zero fluff and working code at every step.