Back to Node.js tutorials
Intermediate15 min read

ES Modules in Node

Use native import/export syntax, package.json module settings, and interoperability with CommonJS libraries.

Enabling ES Modules

Set "type": "module" in package.json to treat .js files as ES modules, or use .mjs extension per file. import and export replace require and module.exports.

__dirname and __require are unavailable; use import.meta.url with fileURLToPath for directory paths.

Dynamic import() loads modules conditionally and returns Promises—useful for plugin systems and lazy loading.

  • Use .cjs extension for legacy CommonJS in hybrid packages
  • Prefer static import for tree shaking and static analysis
  • Configure "exports" field for public package entrypoints
import path from 'node:path';
import { fileURLToPath } from 'node:url';

const __dirname = path.dirname(fileURLToPath(import.meta.url));

CommonJS Interop

Import CommonJS default exports with default import syntax even when the module uses module.exports. Named imports may require createRequire for odd cases.

Dual packages publish both ESM and CJS via conditional exports. Avoid duplicate instance bugs with singleton state.

Test both module systems if publishing libraries consumed widely.

  • Read library docs for ESM compatibility notes
  • Use node --experimental-require-module when migrating gradually
  • Lint import order consistently across the codebase
import pkg from 'legacy-commonjs-package';
import { createRequire } from 'node:module';
const require = createRequire(import.meta.url);

Module Resolution

Node resolves import specifiers to files and node_modules. Subpath imports and import maps (experimental) customize resolution.

TypeScript projects align module and moduleResolution in tsconfig with Node16/NodeNext for accurate emit.

Avoid deep imports into package internals that break when package authors refactor.

  • Use "imports" field for internal package aliases
  • Run tsc or tsx during development for TypeScript ESM
  • Document breaking changes when switching from CJS to ESM

Get In Touch


Ready to discuss your next project? Drop me a message.