package.json Fields Reference

Every package.json field with type, semantics and npm/bundler behavior notes

Searchable reference of package.json fields covering name, version, exports, scripts, dependencies, bin, files, type, and engines. Each entry lists the value type, what npm and bundlers do with it, and common pitfalls. It runs free in your browser on Gera Tools, with nothing uploaded.

Last updated Source: Gera Tools

What is the exports field for?

The exports field defines the public entry points of a package and, when present, hides every other internal file from importers. It supports conditional resolution so you can ship separate ESM and CommonJS builds and map subpaths like ./utils to specific files.

package.json is the manifest npm, bundlers, and Node read to understand your package. A single misplaced field can break imports for every consumer or silently publish your entire working tree. This is a searchable reference of the fields you actually touch, with what each one does and where it bites.

How it works

Each field has a defined value type (string, object, array, or boolean) and is read by a specific tool: npm for install and publish behavior, Node for module resolution, and bundlers for entry-point selection. The reference groups fields by purpose:

  • Identityname, version, private, license.
  • Entry pointsmain, module, types, exports, bin, type.
  • Dependenciesdependencies, devDependencies, peerDependencies, optionalDependencies.
  • Publishingfiles, publishConfig, engines, os, cpu.
  • Toolingscripts, workspaces.

The fields most likely to cause problems in practice

exports encapsulation

When exports is present, it completely overrides main and makes every file in the package unreachable except those explicitly listed. This breaks packages whose consumers import from deep paths like import { helper } from 'pkg/src/utils'. When adding exports to an existing package, audit your consumers for deep imports before publishing — many will break silently if Node cannot find the subpath in your map.

The conditional exports map allows different resolution for different module systems and environments:

"exports": {
  ".": {
    "import": "./dist/index.mjs",
    "require": "./dist/index.cjs",
    "types": "./dist/index.d.ts"
  }
}

type: "module" and file extensions

Setting "type": "module" treats all .js files as ESM. Files that need to stay CommonJS must use .cjs. The reverse is also true: with "type": "commonjs" (the default), ESM files need .mjs. Mixing types without correct extensions causes “require() of ES Module” or “Cannot use import statement” errors that are confusing to diagnose because they depend on which consumers import the package.

peerDependencies vs. dependencies

If your package requires React, listing it as a dependency installs a second copy of React when the consumer already has it — leading to “invalid hook call” errors and similar multiple-instance problems. List it as a peerDependency instead, specifying a compatible version range. The consumer’s own React installation will be used.

files and the publish allowlist

Without a files field, npm publishes everything not in .npmignore or .gitignore. This frequently publishes test directories, build scripts, local config, and environment files. Adding files with an explicit allowlist (typically your dist folder plus a readme and license) keeps the tarball small and avoids accidentally shipping internal tooling.

"files": ["dist", "README.md", "LICENSE"]

engines version constraints

engines is advisory by default — npm warns but does not block installation on mismatched Node versions. To make it a hard block, set engine-strict = true in .npmrc. This is useful for packages that use Node APIs not available in older versions, but be conservative with the minimum version or you will block otherwise compatible users.

Dependency types at a glance

FieldWhen installedUse for
dependenciesAlwaysRuntime requirements
devDependenciesLocal + CI onlyBuild tools, test frameworks
peerDependenciesDeclared but not installedShared singletons (React, Vue, etc.)
optionalDependenciesInstalled if possibleBinaries with OS-specific builds

Tips and gotchas

The modern resolution order is exports first (if present, it overrides main), then main for legacy. Always ship a types or typesVersions mapping so TypeScript consumers get types. Remember that an exports map encapsulates the package — anything not listed becomes unreachable, which catches teams that relied on deep imports. For dual ESM/CJS packages, pair type with conditional exports so the right file loads under import versus require.