Cannot use import statement outside a module, explained and fixed

This SyntaxError means a file that uses import was loaded as a traditional script. Node decides a file's module system from its extension and the nearest package.json, and by default a .js file is CommonJS, where import is illegal. In the browser the same thing happens when a <script> tag lacks type="module". The code is fine; the loading mode is wrong.

Pick the fix that matches the project

  1. Make the package an ES module project: add "type": "module" to package.json. Every .js file in the package is then treated as ESM. Only do this if the whole package is (or should be) ESM, it changes the meaning of every file, including breaking require and __dirname in files that relied on them.
  2. Mark just this file: rename it from .js to .mjs. Node always treats .mjs as an ES module regardless of package settings, the surgical option for one file or a script inside a CommonJS project.
  3. Convert the file to CommonJS: replace the import statements with require calls and export with module.exports, right when the file is a small script inside a CommonJS codebase.
  4. In the browser, add the attribute: <script type="module" src="app.js">. Module scripts are deferred by default and can use import directly.

Traps that follow the first fix

  1. Relative imports in ESM need the extension: import x from './util' fails in Node ESM with a “did not provide an export” or not-found error; write './util.js' in full.
  2. __dirname and __filename do not exist in ES modules; rebuild them from import.meta.url with fileURLToPath when needed.
  3. JSON imports need an import attribute in current Node (with { type: 'json' }); code copied from bundler projects, where JSON imports just work, breaks here.

Common questions

Why did this start after I copied the file into another project? Because the module decision comes from the surrounding package, not the file. The same file is ESM in a "type": "module" project and CommonJS in a default one.

Should I just always set type module? For a new project written in ESM style, yes. For an existing CommonJS project, flipping the flag changes every file at once and usually trades this error for several others; rename the one file to .mjs instead.

Does this affect TypeScript projects? The error appears at runtime from the emitted JavaScript or the loader settings, so check the compiler's module setting and which runtime (Node, ts-node, a bundler) executes the output; they must agree.

Related errors

ReferenceError: require is not defined · Node: Cannot find module · ERR_OSSL_EVP_UNSUPPORTED