package.json — Field by Field
Every Node project has a package.json at its root. It is part manifest, part build configuration, part tiny task runner. Most of the fields are optional; a few are load-bearing. This page walks the ones you will read or edit most often.
name and version
{
"name": "my-app",
"version": "0.1.0"
}name— lowercase, no spaces, URL-safe. Required if you plan to publish. Scoped names look like@org/pkg.version— Semantic Versioning string (MAJOR.MINOR.PATCH). Required if you publish.
type — CommonJS vs ES modules
The type field tells Node how to interpret .js files in this package.
{ "type": "module" } // .js files are ES modules
{ "type": "commonjs" } // (default) .js files are CommonJSWith
"type": "module",.jsfiles useimport/export. Use.cjsto opt a single file back into CommonJS.Without it,
.jsfiles userequire/module.exports. Use.mjsto opt a single file into ESM.
main, module, exports
These three tell the world how to load your package.
main— the entry point for CommonJS consumers and older tooling.module— historical hint for bundlers that this entry uses ES module syntax.exports— the modern, authoritative map. Hides everything you don't list and lets you give different files to ESM vs CJS consumers.
A modern exports map
{
"name": "my-lib",
"type": "module",
"exports": {
".": {
"import": "./dist/index.mjs",
"require": "./dist/index.cjs",
"types": "./dist/index.d.ts"
},
"./utils": "./dist/utils.mjs"
}
}Consumers can import x from "my-lib" or import u from "my-lib/utils", but nothing else — internal files are not reachable.
scripts
A mini task runner. Each entry is a shell command. npm test, npm start and npm run <name> execute them. Special names like pre*/post* run automatically around the matching script.
{
"scripts": {
"dev": "vite",
"build": "vite build",
"test": "vitest run",
"lint": "eslint .",
"prebuild": "npm run lint",
"format": "prettier --write ."
}
}Inside a script, locally-installed binaries (./node_modules/.bin) are on PATH — write vite not ./node_modules/.bin/vite.
dependencies and friends
{
"dependencies": {
"react": "^18.2.0"
},
"devDependencies": {
"vitest": "^1.0.0"
},
"peerDependencies": {
"react": ">=18"
},
"optionalDependencies": {
"fsevents": "^2.3.0"
}
}dependencies— installed when anyone uses your package or runsnpm installin your project.devDependencies— installed in your project, not when others depend on your published package.peerDependencies— the package expects the host project to bring its own copy. Common in plugins.optionalDependencies— install is attempted, failure is fine.
engines
Declare the Node (and npm) versions your package supports. npm install warns when the local version doesn't match; many hosting platforms read it to pick a runtime.
{
"engines": {
"node": ">=18.17",
"npm": ">=9"
}
}files and publishing
files is an allow-list of paths included when you publish. README.md, package.json and LICENSE are always included; node_modules and dotfiles are always excluded.
{
"files": ["dist", "README.md"],
"private": true
}"private": true blocks accidental npm publish. A great default for apps that should never be published.
bin — shipping a CLI
Map a command name to a script in your package, and npm install -g will put it on the user's PATH.
{
"name": "say-hi",
"version": "1.0.0",
"bin": { "hi": "./bin/cli.js" }
}browser, sideEffects and other bundler hints
browser— an alternate entry for bundlers targeting browsers.sideEffects—falsetells bundlers that your modules are safe to tree-shake.workspaces— an array of glob paths for monorepos.
repository, author, license
Metadata for humans and tools. The npm site renders these; CLI tools like npm repo use them to open the right page.
{
"author": "Ada Lovelace <ada@example.com>",
"license": "MIT",
"repository": { "type": "git", "url": "https://github.com/me/my-lib" },
"bugs": "https://github.com/me/my-lib/issues",
"homepage": "https://my-lib.dev"
}