Every Node.js project lives or dies by its package.json. It declares the name, version, dependencies, scripts, and module entry points. npm init creates a minimal one. The minimum is rarely what you actually want.
A tuned package.json saves time for everyone who touches the project. Scripts automate common tasks. The engines field stops version mismatches in CI. A correct exports block keeps the package working in both ESM and CommonJS consumers.
This guide covers the fields that matter in 2026 and the patterns experienced Node teams already use. Internal tool, web app, or open-source package: getting package.json right at the start saves hours of debugging later.
Keep the file readable with the JSON Formatter. Consistent indentation and stable key ordering make the diffs clean in code review.
Fields Beyond `name` and `version`
Everyone fills in name, version, and description. These fields are where the real wins are.
type: "module" for ESM, omit for CommonJS. In 2026 most new projects should ship "type": "module".
engines: the Node and npm versions your project supports.
`json
"engines": {
"node": ">=20.0.0",
"npm": ">=10.0.0"
}
`
With engine-strict=true in .npmrc, this blocks installs on incompatible versions.
packageManager: pin the package manager via Corepack.
`json
"packageManager": "pnpm@9.1.0"
`
exports: modern entry points. Replaces main for dual ESM/CJS packages.
`json
"exports": {
".": {
"import": "./dist/index.mjs",
"require": "./dist/index.cjs",
"types": "./dist/index.d.ts"
}
}
`
files: whitelist of what ships when you publish. Everything else is excluded.
`json
"files": ["dist", "README.md", "LICENSE"]
`
sideEffects: false if your package has no side effects. Bundlers (Webpack, Rollup) use this to tree-shake aggressively.
Validate the whole structure with the JSON Schema Validator against the official package.json schema. It catches typos that produce cryptic npm errors.
Scripts That Pay Off
The scripts field is where most of your workflow lives. A pattern that holds up on real projects:
`json
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "eslint . --ext .ts,.tsx",
"lint:fix": "eslint . --ext .ts,.tsx --fix",
"format": "prettier --write .",
"format:check": "prettier --check .",
"typecheck": "tsc --noEmit",
"test": "vitest run",
"test:watch": "vitest",
"test:coverage": "vitest run --coverage",
"validate": "npm run typecheck && npm run lint && npm run test",
"prepare": "husky",
"precommit": "lint-staged"
}
`
Naming conventions worth copying:
noun:verbfor related commands:test,test:watch,test:coverage.validateruns everything CI runs.prepareruns automatically afternpm install(good for setting up git hooks).
npm runs prescript before script and postscript after. prebuild and postbuild are the common ones. Use them for setup and cleanup, not surprise side effects.
For portability, avoid shell-specific syntax inside scripts. Use cross-env for environment variables and rimraf instead of rm -rf. Otherwise Windows contributors will hate the project.

Dependency Rules
- dependencies vs devDependencies: runtime code goes in
dependencies. Build tools, test runners, linters, and formatters go indevDependencies. Mix them up and production installs balloon. - peerDependencies: for plugins and libraries that must share a dep with the host. A React component library peer-depends on React, never bundles it:
`json
"peerDependencies": {
"react": "^18.0.0 || ^19.0.0"
}
`
- Version ranges:
^1.2.3allows 1.x.x (minor and patch).~1.2.3allows 1.2.x (patch only).1.2.3pins exact. Default to^for most, exact for critical infra. - Lock files: commit
package-lock.json,pnpm-lock.yaml, oryarn.lock. The lock file is what makes installs reproducible across machines and CI. - Audit: run
npm auditregularly. Address high and critical severities fast. Lower severities can ride the normal upgrade cadence. - Overrides: fix a vulnerable transitive dep without waiting on the parent maintainer.
`json
"overrides": {
"vulnerable-package": "^2.0.1"
}
`
- Bundle weight: check
bundlephobia.combefore adding any dependency. A 50 KB utility for a function you could write in ten lines is rarely worth it.
Publishing to npm
For published packages, these fields earn their keep:
name: must be unique on npm. Check withnpm search. Scoped packages (@org/package) sidestep naming conflicts.version: semver. Start at1.0.0for the first stable release. Patch (1.0.1) for bug fixes, minor (1.1.0) for new features, major (2.0.0) for breaking changes.license:"MIT","Apache-2.0","ISC", or"UNLICENSED"for private packages.repository:
`json
"repository": {
"type": "git",
"url": "https://github.com/user/repo.git"
}
`
keywords: 5-10 relevant tags so npm search can find you.
Pre-publish checklist:
npm pack --dry-runto see what would ship and the size.- Verify
filesincludes only what is needed. - Run the test suite one more time.
- Bump the version with
npm version patch | minor | major. npm publish(ornpm publish --access publicfor scoped packages).
Automate with semantic-release or changesets so commit messages drive the version. Manual version management ends in mistakes.

FAQ
npm, yarn, or pnpm?
All three are production-ready in 2026. pnpm is fastest and the most disk-efficient. yarn has the strongest monorepo workspaces. npm is the default and ships with Node. Pick one per team and lock it via the packageManager field.
What does the caret (^) actually mean?
The caret allows updates that do not change the leftmost non-zero digit. ^1.2.3 allows 1.2.3 up to (but not including) 2.0.0. ^0.2.3 allows 0.2.3 up to 0.3.0 only. This mirrors semver's rule that major bumps signal breaking changes.
How do I make a package work with both `require()` and `import`?
Use conditional exports with both import (ESM) and require (CJS) entries. Build dual output with tsup, unbuild, or esbuild. Test both entry points before you publish; subtle bugs only show up in one or the other.
Should I commit `node_modules`?
No. Put node_modules/ in .gitignore and commit the lock file. Reproducible installs without the cost of versioning thousands of dependency files.
Markdown Table Generator: Build Clean Tables Without the Pain
Markdown tables are simple until the pipes and dashes stop lining up. Learn the syntax, alignment tricks, and a free tool that formats tables for you.
CSV to JSON: Convert Spreadsheet Data for APIs and Code
Turn a CSV export into clean JSON for APIs, imports, and scripts. Learn how the conversion works, common pitfalls with types and quotes, and a free tool.
JSON Guide: Format, Validate, and Convert JSON Files
JSON guide for developers: syntax rules, common parse errors, formatting and schema validation, plus how to convert between JSON and CSV files.
