Modules: ECMAScript modules | Node.js v25.9.0 Documentation Skip to content Node.js About this documentation Usage and example Assertion testing Asynchronous context tracking Async hooks Buffer C++ addons C/C++ addons with Node-API C++ embedder API Child processes Cluster Command-line options Console Crypto Debugger Deprecated APIs Diagnostics Channel DNS Domain Environment Variables Errors Events File system Globals HTTP HTTP/2 HTTPS Inspector Internationalization Modules: CommonJS modules Modules: ECMAScript modules Modules: node:module API Modules: Packages Modules: TypeScript Net Iterable Streams API OS Path Performance hooks Permissions Process Punycode Query strings Readline REPL Report Single executable applications SQLite Stream String decoder Test runner Timers TLS/SSL Trace events TTY UDP/datagram URL Utilities V8 VM WASI Web Crypto API Web Streams API Worker threads Zlib Zlib Iterable Compression Code repository and issue tracker Table of contents Modules: ECMAScript modules Introduction Enabling Packages import Specifiers Terminology Mandatory file extensions URLs file: URLs data: imports node: imports Import attributes Built-in modules import() expressions import.meta import.meta.dirname import.meta.filename import.meta.url import.meta.main import.meta.resolve(specifier) Interoperability with CommonJS import statements require CommonJS Namespaces Differences between ES modules and CommonJS No require exports , or module.exports No __filename or __dirname No Addon Loading No require.main No require.resolve No NODE_PATH No require.extensions No require.cache JSON modules Wasm modules Wasm Source Phase Imports JavaScript String Builtins Wasm Instance Phase Imports Reserved Wasm Namespaces Top-level await Loaders Resolution and loading algorithm Features Resolution algorithm Resolution Algorithm Specification Customizing ESM specifier resolution algorithm Modules: ECMAScript modules Added in: v8.5.0 History Version Changes v23.1.0, v22.12.0, v20.18.3, v18.20.5 Import attributes are no longer experimental. v22.0.0 Drop support for import assertions. v21.0.0, v20.10.0, v18.20.0 Add experimental support for import attributes. v20.0.0, v18.19.0 Module customization hooks are executed off the main thread. v18.6.0, v16.17.0 Add support for chaining module customization hooks. v17.1.0, v16.14.0 Add experimental support for import assertions. v17.0.0, v16.12.0 Consolidate customization hooks, removed getFormat getSource transformSource , and getGlobalPreloadCode hooks added load and globalPreload hooks allowed returning format from either resolve or load hooks. v15.3.0, v14.17.0, v12.22.0 Stabilize modules implementation. v14.13.0, v12.20.0 Support for detection of CommonJS named exports. v14.8.0 Unflag Top-Level Await. v14.0.0, v13.14.0, v12.20.0 Remove experimental modules warning. v13.2.0, v12.17.0 Loading ECMAScript modules no longer requires a command-line flag. v12.0.0 Add support for ES modules using .js file extension via package.json "type" field. Stability: 2 - Stable Introduction ECMAScript modules are the official standard format to package JavaScript code for reuse. Modules are defined using a variety of import and export statements. The following example of an ES module exports a function: // addTwo.mjs function addTwo num return num export addTwo }; The following example of an ES module imports the function from addTwo.mjs // app.mjs import addTwo from './addTwo.mjs' // Prints: 6 console log addTwo )) Node.js fully supports ECMAScript modules as they are currently specified and provides interoperability between them and its original module format, CommonJS Enabling Node.js has two module systems: CommonJS modules and ECMAScript modules. Authors can tell Node.js to interpret JavaScript as an ES module via the .mjs file extension, the package.json "type" field with a value "module" or the --input-type flag with a value of "module" . These are explicit markers of code being intended to run as an ES module. Inversely, authors can explicitly tell Node.js to interpret JavaScript as CommonJS via the .cjs file extension, the package.json "type" field with a value "commonjs" , or the --input-type flag with a value of "commonjs" When code lacks explicit markers for either module system, Node.js will inspect the source code of a module to look for ES module syntax. If such syntax is found, Node.js will run the code as an ES module; otherwise it will run the module as CommonJS. See Determining module system for more details. Packages This section was moved to Modules: Packages import Specifiers Terminology The specifier of an import statement is the string after the from keyword, e.g. 'node:path' in import { sep } from 'node:path' . Specifiers are also used in export from statements, and as the argument to an import() expression. There are three types of specifiers: Relative specifiers like './startup.js' or '../config.mjs' . They refer to a path relative to the location of the importing file. The file extension is always necessary for these. Bare specifiers like 'some-package' or 'some-package/shuffle' . They can refer to the main entry point of a package by the package name, or a specific feature module within a package prefixed by the package name as per the examples respectively. Including the file extension is only necessary for packages without an "exports" field. Absolute specifiers like 'file:///opt/nodejs/config.js' . They refer directly and explicitly to a full path. Bare specifier resolutions are handled by the Node.js module resolution and loading algorithm All other specifier resolutions are always only resolved with the standard relative URL resolution semantics. Like in CommonJS, module files within packages can be accessed by appending a path to the package name unless the package's package.json contains an "exports" field, in which case files within packages can only be accessed via the paths defined in "exports" For details on these package resolution rules that apply to bare specifiers in the Node.js module resolution, see the packages documentation Mandatory file extensions A file extension must be provided when using the import keyword to resolve relative or absolute specifiers. Directory indexes (e.g. './startup/index.js' must also be fully specified. This behavior matches how import behaves in browser environments, assuming a typically configured server. URLs ES modules are resolved and cached as URLs. This means that special characters must be percent-encoded , such as with %23 and with %3F file: node: , and data: URL schemes are supported. A specifier like 'https://example.com/app.js' is not supported natively in Node.js unless using custom HTTPS loader file: URLs Modules are loaded multiple times if the import specifier used to resolve them has a different query or fragment. import './foo.mjs?query=1' // loads ./foo.mjs with query of "?query=1" import './foo.mjs?query=2' // loads ./foo.mjs with query of "?query=2" The volume root may be referenced via // , or file:/// . Given the differences between URL and path resolution (such as percent encoding details), it is recommended to use url.pathToFileURL when importing a path. data: imports Added in: v12.10.0 data: URLs are supported for importing with the following MIME types: text/javascript for ES modules application/json for JSON application/wasm for Wasm import 'data:text/javascript,console.log("hello!");' import from 'data:application/json,"world!"' with type 'json' }; data: URLs only resolve bare specifiers for builtin modules and absolute specifiers . Resolving relative specifiers does not work because data: is not a special scheme . For example, attempting to load ./foo from data:text/javascript,import "./foo"; fails to resolve because there is no concept of relative resolution for data: URLs. node: imports Added in: v14.13.1, v12.20.0 History Version Changes v16.0.0, v14.18.0 Added node: import support to require(...) node: URLs are supported as an alternative means to load Node.js builtin modules. This URL scheme allows for builtin modules to be referenced by valid absolute URL strings. import fs from 'node:fs/promises' Import attributes Added in: v17.1.0, v16.14.0 History Version Changes v21.0.0, v20.10.0, v18.20.0 Switch from Import Assertions to Import Attributes. Import attributes are an inline syntax for module import statements to pass on more information alongside the module specifier. import fooData from './foo.json' with type 'json' }; const default barData await import './bar.json' with type 'json' Node.js only supports the type attribute, for which it supports the following values: Attribute type Needed for 'json' JSON modules The type: 'json' attribute is mandatory when importing JSON modules. Built-in modules Built-in modules provide named exports of their public API. A default export is also provided which is the value of the CommonJS exports. The default export can be used for, among other things, modifying the named exports. Named exports of built-in modules are updated only by calling module.syncBuiltinESMExports() import EventEmitter from 'node:events' const new EventEmitter () import readFile from 'node:fs' readFile './foo.txt' err source => if (err) console error (err) else console log (source) import fs readFileSync from 'node:fs' import syncBuiltinESMExports from 'node:module' import Buffer from 'node:buffer' fs readFileSync () => Buffer from 'Hello, ESM' syncBuiltinESMExports () fs readFileSync === readFileSync When importing built-in modules, all the named exports (i.e. properties of the module exports object) are populated even if they are not individually accessed. This can make initial imports of built-in modules slightly slower compared to loading them with require() or process.getBuiltinModule() , where the module exports object is evaluated immediately, but some of its properties may only be initialized when first accessed individually. import() expressions Dynamic import() provides an asynchronous way to import modules. It is supported in both CommonJS and ES modules, and can be used to load both CommonJS and ES modules. import.meta Type: