Common causes
- Node project without "type": "module" in package.json, running a .js file that uses import
- In the browser, a <script src="app.js"> tag missing type="module"
- Jest running untransformed ESM code, or a dependency in node_modules that ships only ESM
- A TypeScript build outputs ES modules (module: ESNext) but the result is run as CommonJS, or vice versa
- Running a TypeScript file directly with node or an old ts-node setup
- A WordPress theme or plugin enqueueing a file that uses import as a normal script
How to fix it
- Decide which module system you want. New Node 18+ projects can use ES modules throughout. Older projects and many tools still expect CommonJS; mixing both in one file is what triggers this error.
- Node: mark the package as ESM. Add "type": "module" to package.json, or rename the file to .mjs. Then use import everywhere in it and include file extensions in relative imports.
- Or switch the file to CommonJS. If the rest of the project uses require(), change import x from 'x' to const x = require('x'), or rename ESM files to .mjs and keep the others .cjs.
- Browser: use type="module". Load the script with <script type="module" src="/js/app.js"></script>. Module scripts are deferred automatically and must be served over HTTP(S), not opened via file://.
- Jest: transform or use native ESM. Use babel-jest or ts-jest so test files are transformed, add ESM-only packages to transformIgnorePatterns exceptions, or run Jest with node --experimental-vm-modules.
- WordPress: add the module attribute. On WordPress 6.5+ use wp_enqueue_script_module(); on older versions add type="module" via the script_loader_tag filter, or bundle the code into a classic script.
package.json + index.js (Node 18+)
// package.json
{
"name": "my-app",
"type": "module"
}
// index.js
import express from 'express';
import { routes } from './routes.js'; // extension required How to stop it happening again
- Choose ESM or CommonJS for the whole project and set "type" in package.json explicitly
- Use .mjs/.cjs extensions for the exceptions
- Keep TypeScript's module and moduleResolution settings (e.g. NodeNext) aligned with how the output runs