Skip to main content
ES modules are the official standard format for JavaScript modules. They use import and export statements.

Introduction

ECMAScript modules (ESM) are the standard for packaging JavaScript code for reuse. They are supported natively in Node.js and modern browsers.

Basic Example

// math.mjs
export function add(a, b) {
  return a + b;
}

export const PI = 3.14159;
// app.mjs
import { add, PI } from './math.mjs';

console.log(add(2, 3)); // 5
console.log(PI); // 3.14159

Enabling ES Modules

Node.js treats files as ES modules when:
  1. Files have .mjs extension
  2. Files have .js extension with "type": "module" in nearest package.json
  3. String input with --input-type=module flag

Using .mjs Extension

// module.mjs - treated as ES module
import fs from 'node:fs';

Using package.json

// package.json
{
  "type": "module"
}
// module.js - treated as ES module when package.json has "type": "module"
import fs from 'node:fs';

Import Syntax

Named Imports

import { readFile, writeFile } from 'node:fs/promises';
import { join } from 'node:path';

Default Import

import fs from 'node:fs';
import express from 'express';

Namespace Import

import * as fs from 'node:fs';
import * as path from 'node:path';

console.log(fs.readFileSync);
console.log(path.join);

Dynamic Import

// Works in both CommonJS and ES modules
const module = await import('./my-module.mjs');

// Conditional loading
if (condition) {
  const { feature } = await import('./feature.mjs');
}

Export Syntax

Named Exports

export const name = 'John';
export function greet() {
  return 'Hello!';
}
export class User {}

Default Export

export default class Application {
  constructor() {
    this.name = 'MyApp';
  }
}

Export List

const a = 1;
const b = 2;
function c() {}

export { a, b, c };

Re-exporting

export { something } from './other.mjs';
export * from './utilities.mjs';

Import Specifiers

Relative Specifiers

Must include file extension:
import './startup.js';
import config from './config.json' with { type: 'json' };
import utils from '../lib/utils.mjs';

Bare Specifiers

Package names or built-in modules:
import express from 'express';
import { EventEmitter } from 'node:events';

Absolute Specifiers

import config from 'file:///opt/app/config.js';

Import Attributes

Provide metadata about imports:

JSON Modules

import data from './data.json' with { type: 'json' };
console.log(data);
The with { type: 'json' } attribute is required for importing JSON files.

import.meta

Metadata about the current module:

import.meta.url

console.log(import.meta.url);
// file:///home/user/project/module.mjs

import.meta.filename

console.log(import.meta.filename);
// /home/user/project/module.mjs

import.meta.dirname

console.log(import.meta.dirname);
// /home/user/project

import.meta.resolve()

Resolve module specifiers:
const resolved = import.meta.resolve('./lib/feature.js');
console.log(resolved);
// file:///home/user/project/lib/feature.js

const pkgPath = import.meta.resolve('express');
// file:///home/user/project/node_modules/express/index.js

import.meta.main

Check if module is the entry point:
export function greet() {
  console.log('Hello!');
}

if (import.meta.main) {
  // Run only when this is the entry point
  greet();
}

Loading CommonJS from ES Modules

Import CommonJS

// CommonJS module: circle.cjs
exports.area = (r) => Math.PI * r ** 2;
// ES module
import pkg from './circle.cjs';
console.log(pkg.area(4));

// Named imports (if detected)
import { area } from './circle.cjs';
console.log(area(4));

Top-level await

Use await at the top level of ES modules:
const response = await fetch('https://api.example.com/data');
const data = await response.json();

export { data };
import { data } from './fetchData.mjs';
console.log(data); // Already resolved

Built-in Modules

Import Node.js built-in modules:
import fs from 'node:fs/promises';
import path from 'node:path';
import { EventEmitter } from 'node:events';

const content = await fs.readFile('./file.txt', 'utf-8');

Default and Named Exports

// Default export
import fs from 'node:fs';

// Named exports
import { readFile } from 'node:fs/promises';

// Both
import fs, { readFileSync } from 'node:fs';

Data URLs

Import from data URLs:
import 'data:text/javascript,console.log("hello");';

import data from 'data:application/json,"world"' with { type: 'json' };
console.log(data); // "world"

WebAssembly Modules

Import WebAssembly modules:
// Import WebAssembly module instance
import * as wasm from './module.wasm';
wasm.exportedFunction();

// Source phase import (get the WebAssembly.Module)
import source wasmModule from './module.wasm';
const instance = await WebAssembly.instantiate(wasmModule, imports);

Differences from CommonJS

No require()

Use import instead of require():
// CommonJS
const fs = require('fs');

// ES modules
import fs from 'node:fs';

// Or create require() if needed
import { createRequire } from 'node:module';
const require = createRequire(import.meta.url);
const pkg = require('./package.json');

No __dirname or __filename

Use import.meta:
// CommonJS
console.log(__dirname);
console.log(__filename);

// ES modules
console.log(import.meta.dirname);
console.log(import.meta.filename);

No module.exports

Use export statements:
// CommonJS
module.exports = { hello: 'world' };

// ES modules
export default { hello: 'world' };
// or
export const hello = 'world';

Module Resolution

File Extensions Required

// ❌ This won't work
import utils from './utils';

// ✅ This works
import utils from './utils.js';

No Directory Indexes

// ❌ This won't work
import app from './lib';

// ✅ Must specify the full path
import app from './lib/index.js';

Package Exports

Use package.json exports field:
{
  "name": "my-package",
  "exports": {
    ".": "./index.js",
    "./utils": "./lib/utils.js"
  }
}
import pkg from 'my-package';
import { helper } from 'my-package/utils';

Conditional Exports

Provide different entry points based on environment:
{
  "exports": {
    "import": "./index.mjs",
    "require": "./index.cjs"
  }
}

Customization Hooks

Customize module loading with hooks:
import { registerHooks } from 'node:module';

registerHooks({
  resolve(specifier, context, nextResolve) {
    // Custom resolution logic
    return nextResolve(specifier, context);
  },
  load(url, context, nextLoad) {
    // Custom loading logic
    return nextLoad(url, context);
  }
});

CommonJS Modules

Legacy CommonJS module system with require()

node:module API

Module utilities and customization hooks

Packages

Package.json configuration and exports