DevBolt
Processed in your browser. Your data never leaves your device.

ESM vs CommonJS in package.json

The type field in package.json determines how Node.js treats .js files. Use the generator above to switch between module (ESM) and commonjs (CJS) and see how the output changes.

Basic Info

Module Config

devnext dev
buildnext build
startnext start
lintnext lint
next^15.0.0
react^19.0.0
react-dom^19.0.0
typescript^5.7.0
@types/node^22.0.0
@types/react^19.0.0
@types/react-dom^19.0.0
eslint^9.0.0
eslint-config-next^15.0.0
package.json
{
  "name": "my-nextjs-app",
  "version": "0.1.0",
  "private": true,
  "type": "module",
  "main": "index.js",
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint"
  },
  "dependencies": {
    "next": "^15.0.0",
    "react": "^19.0.0",
    "react-dom": "^19.0.0"
  },
  "devDependencies": {
    "typescript": "^5.7.0",
    "@types/node": "^22.0.0",
    "@types/react": "^19.0.0",
    "@types/react-dom": "^19.0.0",
    "eslint": "^9.0.0",
    "eslint-config-next": "^15.0.0"
  },
  "license": "MIT"
}

4

Scripts

3

Deps

6

DevDeps

Press Ctrl+Enter to copy

What type: module does

Setting "type": "module" tells Node.js to treat .js files as ES modules. This enables import/export syntax, top-level await, and import.meta. Without it, Node.js treats .js as CommonJS (require/module.exports). You can override per-file with .mjs (always ESM) and .cjs (always CJS).

Key differences in practice

ESM: import/export, top-level await, import.meta.url instead of __filename, no require(). CJS: require/module.exports, __filename/__dirname, synchronous loading, JSON import via require(). ESM is strict by default, CJS is sloppy mode. ESM imports are live bindings, CJS exports are value copies.

Migrating from CJS to ESM

Steps: 1) Set "type": "module" in package.json. 2) Rename .js to .mjs for gradual migration, or convert all at once. 3) Replace require() with import. 4) Replace module.exports with export. 5) Replace __dirname with path.dirname(fileURLToPath(import.meta.url)). 6) Add file extensions to relative imports.

Publishing dual-format packages

Dual packages support both CJS and ESM consumers. Use the exports field with conditional exports: 'import' points to ESM, 'require' points to CJS. Build tools like tsup, unbuild, or pkgroll generate both formats from the same TypeScript source. Test both entry points before publishing.

Frequently Asked Questions

Should new projects use ESM or CommonJS?

New projects should use ESM ("type": "module"). ESM is the JavaScript standard, supported by all modern browsers and Node.js 14+. CJS is legacy. The ecosystem is rapidly moving to ESM-only — major packages like chalk, got, and execa are ESM-only.

Can I use require() in an ESM project?

Not directly in .js files when type is module. You can use createRequire from the 'module' built-in, or use dynamic import() which works in both ESM and CJS. For JSON files, use import with the assert syntax or createRequire.

What if my dependency is CJS-only?

ESM can import CJS modules using default import syntax: 'import pkg from "cjs-package"'. Named exports may not work — use default import and destructure. This is a Node.js interop feature that makes migration smoother.

Related Generate Tools