A Complete File
{
"name": "my-project",
"version": "1.0.0",
"description": "A short sentence about the project",
"private": true,
"type": "module",
"main": "index.js",
"scripts": {
"dev": "http-server -p 3000",
"build": "node scripts/build.js",
"start": "npm run build && npm run dev"
},
"dependencies": {},
"devDependencies": {
"http-server": "^14.1.1"
},
"keywords": [],
"author": "Your Name",
"license": "MIT"
} Only name and version are strictly required, and even those matter mainly to packages that get published. A project that stays private can carry a mostly empty file and work perfectly.
Identity Fields
name
The name must be lowercase with no spaces, though hyphens are fine. It defaults to the folder name. For a published package, this name must be unique across the registry, but for a private project it acts as a simple label.
version
This field uses three numbers separated by dots, representing major, minor, and patch releases. It is meaningful for published packages, but for a private website it serves as decoration.
description
This is a one-sentence description shown on the registry page for published packages. It is harmless and useful to fill in for any project.
author, license, keywords
These are metadata fields for the registry. The command npm init -y fills the license field with ISC, which is a real open source license but might not be the one you want. You can set it deliberately or configure the project as private instead.
Behavior Fields
private
When this is set to true, npm refuses to publish the project to the public registry. Setting this is best for any private project to prevent accidental publishing.
type
When this is set to "module", the .js files in the project use import and export. Omitting this field makes files use the older require style. Mixing these two patterns produces confusing error messages about unexpected tokens.
main
This field specifies the entry point of the package when imported by another project. The npm init command writes this field whether the target file exists or not, and it remains irrelevant for a website that is not imported.
engines
This field specifies the Node versions that the project supports. Deploy platforms read this to select a matching Node runtime, which makes it useful.
browserslist
This field indicates which browsers to support. Several build tools read it to decide how far to translate modern CSS and JavaScript. It is absent unless a tool explicitly requests it.
workspaces
This field configures repositories that hold several packages at once. A single website project does not need it.
scripts
Scripts are a set of named shortcuts. Each value is a command handed to the shell, so shell features like &&, >, and | work inside them.
Inside a script, commands installed into node_modules are found automatically. That is the reason scripts exist rather than typing the tool's name directly.
npm run name
This command runs the script called name, and works for any script.
npm start and npm test
These are two script names npm treats specially. They run without the word run, whereas other scripts require it.
npm run with no name
This command lists every script defined in the file. It is the fastest way to see what a freshly cloned project can do.
Scripts calling scripts
A value like "start": "npm run build && npm run dev" is common and encouraged. Building the big step out of small named ones keeps things organized.
pre and post prefixes
A script named prebuild runs automatically before build, while postbuild runs afterward. This behavior is convenient, but it is easy to miss when reading a file.
Quoting
The value is JSON, so a quote mark inside it has to be escaped with a backslash. This is the usual source of a script that looks right and behaves wrong.
dependencies And devDependencies
dependencies
This is code that the finished product needs at runtime. Running npm install name adds packages here.
devDependencies
These are tools used only while developing or building. Running npm install --save-dev name adds packages here, and nearly everything in a static site project belongs in this list.
peerDependencies
This list is used by plugins to specify which host packages they attach to. You will usually read this in other files rather than writing it yourself.
optionalDependencies
These are optional packages whose install failure does not stop the installation. Using this list is rare.
Version Range Syntax
The string next to a package name is a rule about which versions are acceptable, not a fixed choice. The exact version that got installed lives in package-lock.json.
"^14.1.1" minor and patch updates allowed: 14.9.9 yes, 15.0.0 no
"~14.1.1" patch updates only: 14.1.9 yes, 14.2.0 no
"14.1.1" exactly this version, nothing else
"*" any version at all (avoid) The caret is npm's default because semantic versioning promises that minor and patch releases do not break existing behavior. That promise is a convention rather than an enforced rule, which is one more reason the lock file gets committed.
Files That Sit Next To It
package-lock.json
npm generates this file. It records the exact resolved version and an integrity checksum for every package. You do not hand-edit this file, but you do commit it.
node_modules/
This folder contains the downloaded packages themselves. You can rebuild it from the files above, and it is never committed.
.npmrc
This file holds npm configuration for the project. Most projects do not have one.
.gitignore
This is a git configuration file rather than an npm file, and it is where you exclude node_modules and the build output folder from git.