When I first started learning web development, the package.json file honestly intimidated me.
I could read what it said, but it never made much sense. Words like dependencies and scripts just floated around in my head with no real meaning.
I didn’t have the right resources or anyone to explain what was actually going on.
After working on a bunch of projects and breaking a few along the way, I finally started to understand what this file really does.
Here’s everything I know about it now.
What is package.json?
It’s like your project’s identity card. It lives in the main folder and tells Node.js what your project is, what it needs, and how to run it.
Every serious JavaScript or Node project has one, even if you never touch it directly.
A Practical Example
Let’s start with a real-life package.json file form my Colorful library.
{
"name": "colorful",
"version": "1.6.1",
"description": "I wanted a single library that could convert, manipulate, and generate colors easily from RGB to HEX, from bright to dull, from one color to a full palette. When I couldn’t find one that did everything, I built this.",
"main": "src/colorful.js",
"directories": {
"doc": "docs"
},
"scripts": {
"test": "mocha tests/*.js",
"build": "rollup --config"
},
"type": "module",
"keywords": [
"color manipulation",
"javascript"
],
"author": "Nitish Kumar",
"license": "MIT",
"dependencies": {},
"devDependencies": {
"@rollup/plugin-commonjs": "^28.0.2",
"@rollup/plugin-node-resolve": "^16.0.0",
"@rollup/plugin-replace": "^6.0.2",
"@rollup/plugin-terser": "^0.4.4",
"chai": "^5.1.2",
"mocha": "^10.8.2",
"rollup": "^4.29.1"
}
}
The name, and description provide basic information about your project. The license lets people know how they can use your code.
Now it is time to dive deeper into other keywords to get a better understanding of the whole thing.
version
This shows which version your own project is currently on.
Developers usually follow something called semantic versioning, which looks like this:
1.0.0 → major.minor.patch
- major – for big breaking changes
- minor – for new features that don’t break old ones
- patch – for small bug fixes
It’s not mandatory to follow this, but it helps if you share your code or release updates later.
main
This tells Node.js which file to run first when someone imports or runs your project as a package.
For example, if your main file is index.js, then this line means:
“Start reading my project from here.”
If you’re building a small app for yourself, this doesn’t matter much.
But if you publish an npm package, main tells users which file to load when they require() or import it.
scripts
This section is basically a list of custom commands you can run through npm.
Each entry is a shortcut. For example:
"scripts": {
"test": "mocha tests/*.js",
"build": "rollup --config"
}
Then you can run these like:
npm run build
npm run test
This saves you from typing long terminal commands every time.
For test and start scripts, you can simply type npm start and npm test and skip run to get the same result.
You can create any script name you want. Even weird ones like "deploy:prod": "echo Deploying...".
dependencies
These are the packages your app actually needs to run.
For example, if you’re using Express to handle routes, it goes here:
"dependencies": {
"express": "^4.18.2"
}
When someone installs your project using npm install, Node reads this section and automatically downloads all these packages.
That little ^ symbol before the version means “install the latest minor or patch update.”
For example, ^4.18.2 will install 4.19.5 if available but not 5.0.0 because that is a major version bump.
You could also use ~ to update safely within the same minor version or avoid any symbols if you want to install the exact specified version.
It’s a way of keeping things fresh without breaking your code.
devDependencies
These are the tools you need while building, but not when your app is running.
Things like linters, bundlers, or test frameworks live here.
"devDependencies": {
"eslint": "^8.56.0",
"vite": "^5.0.0"
}
If you deploy your app somewhere, these usually aren’t included because they’re not required for production.
You can install a dev dependency with:
npm install eslint --save-dev
That --save-dev flag tells npm to keep it in devDependencies.
type
This tells Node.js how to interpret your .js files. Whether they should use the old CommonJS syntax or the modern ES Module syntax.
For example:
"type": "module"
tells Node to expect modern import and export syntax:
import express from 'express';
export default app;
If you leave it out, Node defaults to CommonJS mode, which uses require() and module.exports instead:
const express = require('express');
module.exports = app;
So in short:
"type": "module"→ all.jsfiles are treated as ES modules."type": "commonjs"or no type → all.jsfiles are treated as CommonJS..mjsfiles are always modules, and.cjsfiles are always CommonJS, no matter what this setting says.
This field decides how Node (not your browser) runs your code.