ES modules
The Node.js documentation explicitly describes ES modules as "the official standard format to package JavaScript code for reuse". This is not marketing language but a functional definition: ES modules are the accepted format for modern JavaScript applications. Developers split code into logical units, with each module responsible for specific functionality. This promotes maintainability, testability, and code reuse.
Steps: Enabling ES modules in Node.js
Step 1: Check your Node.js version
node --versionFor full support without experimental warnings, you need Node.js v14.0.0 or higher. From v12.17.0 and v13.2.0, loading ES modules works without a command-line flag, but Node.js still shows an 'experimental' warning.
Step 2: Choose your file extension
Use the `.mjs` extension for modules, or set `"type": "module"` in your `package.json` file. The `.mjs` extension is the simplest way to indicate that a file is an ES module.touch app.mjs
Step 3: Configure your package.json
If you want to use `.js` files for ES modules, add the `"type"` field to your `package.json`:{
"name": "mijn-project",
"version": "1.0.0",
"type": "module"
}This tells Node.js that all `.js` files in the project are ES modules. Without this configuration, Node.js treats `.js` files as CommonJS.
Step 4: Export a function from a module
Create a file `utils.mjs` with an export:export function getServerStatus() {
return {
hostname: 'pixzo-server-01',
status: 'online',
uptime: process.uptime()
};
}</p><p>export const config = {
port: 8080,
environment: 'production'
};The `export` statement makes functions and variables available to other modules.
Step 5: Import the module in your main script
Create a file `app.mjs` and import the functions:import { getServerStatus, config } from './utils.mjs';</p><p>console.log('Server status:', getServerStatus());
console.log('Configuration:', config);The `import` statement pulls specific exports from the module. Node.js performs a static analysis of all `import` and `export` statements before the code is executed.
Step 6: Run the script
node app.mjsFrom Node.js v14.0.0, this runs without warnings or flags.
Step 7: Use dynamic imports for runtime loading
ES modules support dynamic imports via `import()`, which allows runtime loading of modules:async function loadModule(moduleName) {
const module = await import(`./modules/${moduleName}.mjs`);
return module.default;
}</p><p>const serverModule = await loadModule('server');This is especially useful for code-splitting and loading modules based on runtime conditions.
Step 8: Combine with CommonJS where needed
For existing CommonJS projects, you can provide a fallback:// file: legacy.cjs
module.exports = {
getLegacyData() {
return { format: 'commonjs' };
}
};// file: modern.mjs
import { createRequire } from 'module';
const require = createRequire(import.meta.url);
const legacy = require('./legacy.cjs');</p><p>console.log(legacy.getLegacyData());The `createRequire` function makes it possible to load CommonJS modules from an ES module.
Verification
Check that your project works correctly with ES modules:node --input-type=module -e "import { getServerStatus } from './utils.mjs'; console.log(getServerStatus());"This command runs an ES module directly from the command line. If you get no error message and see the server status, your ES modules work correctly.
Differences with CommonJS
The difference between ES modules and CommonJS is not only in syntax but also in underlying operation. CommonJS loads synchronously and is based on `require()`. ES modules work asynchronously and are based on static analysis of `import` and `export` statements.Static analysis enables tree-shaking: bundlers like Webpack or Vite can remove unused code. This results in smaller bundles and better performance. CommonJS does not offer this advantage because loading happens dynamically.
ES modules work both server-side and client-side. Because they are part of the ECMAScript standard, modern browsers support the same syntax as Node.js. This simplifies full-stack JavaScript development: the same code or libraries run in the browser and on the server.
Common problems
Problem 1: "Cannot use import statement outside a module"
This happens when Node.js treats a `.js` file as CommonJS. Solve it by using the `.mjs` extension or adding `"type": "module"` to your `package.json`.Problem 2: "ERR_MODULE_NOT_FOUND" on imports
This means that Node.js cannot find the module path. Check whether the path is correct and whether you have added the file extension. Unlike CommonJS, ES modules always require the full file name including the extension:// Wrong - does not work
import { config } from './utils';</p><p>// Correct
import { config } from './utils.mjs';
Problem 3: Experimental warning on older Node.js versions
If you use Node.js v12.17.0 to v13.14.0, you will still get an 'ExperimentalWarning'. This is cosmetic and does not affect functionality. Upgrade to v14.0.0 or higher to remove the warning.Historical context
The support of ES modules in Node.js has developed gradually. Node.js version v8.5.0 introduced the first experimental support, but only with a command-line flag. From v12.0.0, you could enable ES modules without a flag, provided the file had the `.mjs` extension or `"type": "module"` was set in `package.json`.From v12.17.0 and v13.2.0, loading was no longer dependent on a command-line flag. The experimental status remained visible through warnings, but the functionality was technically stable. The final recognition came with Node.js v14.0.0, plus the backports to v13.14.0 and v12.20.0. From these versions, the experimental warning disappeared completely.
The ECMAScript standard itself is managed by Ecma International. The origin of JavaScript lies with Brendan Eich, who developed the language at Netscape under the name Mocha, later LiveScript, and eventually JavaScript. The introduction of modules in the ES2015 specification addressed a fundamental shortcoming: the lack of a built-in way to modularize code.
Practical implications for Pixzo environments
Within the context of Pixzo, with a focus on Linux, servers, hosting, networks, containers and databases, knowledge about ES modules is relevant for anyone working with modern JavaScript applications on the server. The use of ES modules is no longer an optional choice but a standard practice in newer projects. It affects choices around build tools, package managers, deployment processes and compatibility with older systems.In container environments, Node.js often runs as part of a larger application stack. The choice for ES modules has consequences for how you build your Docker images, which Node.js version you base on, and how you bundle your application for production. The static analysis of ES modules makes it possible to generate smaller bundles, which offers direct benefits for container images: less storage, faster pulls and shorter startup times.
The transition from CommonJS to ES modules remains a current topic in the Node.js community. Many existing packages still offer CommonJS as the primary format, while newer packages support both formats through conditional exports in `package.json`. This transition period requires conscious choices when setting up new projects and migrating existing codebases.