Node.js — Cannot use import statement outside a module
Fix "Cannot use import statement outside a module" by aligning the package type, file extension and tooling with ES modules.
Symptoms
- SyntaxError: Cannot use import statement outside a module
- The file works under ts-node but fails under plain node
- Adding "type": "module" breaks require calls elsewhere
- A third-party package uses CommonJS while the project uses ESM
Possible causes
- The project is CommonJS but the file uses ESM import syntax
- The file extension is .js while it should be .mjs, or the package type is not module
- A build step that transpiles was skipped
- Mixing require and import in one file
Diagnosis steps
Check package type and extension
node -p "require('./package.json').type" ; ls package.json *.mjs *.cjsInterpretation
type module makes .js files ESM. Without it, .js is CommonJS and import syntax is invalid.
Reproduce with an explicit extension
mv file.js file.mjs && node file.mjsInterpretation
If renaming to .mjs fixes it, the runtime was treating the file as CommonJS.
Look for mixed module syntax
grep -n "require(\|import " file.jsInterpretation
Mixing require and import in an ESM file fails because require is not defined there.
Root cause
The runtime classified the file as CommonJS, so ESM import syntax was a syntax error. The module system is chosen by extension and package type, not by the syntax used inside the file.
Fix
Set "type": "module" for an ESM project, use .mjs for individual ESM files, or transpile the source as part of the build. Avoid mixing require and import in the same file.
Verify the fix
Run the file again and confirm it parses and executes, then run the whole test suite to confirm other modules still load.
Symptom walkthrough
Running a file that uses import syntax fails with SyntaxError: Cannot use import statement outside a module. The file may work under a transpiler or test runner that implicitly enables ESM, hiding the problem until it runs under plain node or another tool. The error is about how the runtime classified the file, not about the import line itself.
Fix in practice
Choose a module strategy and apply it consistently: set type module in package.json for an ESM project, rename individual files to .mjs, or keep CommonJS and use require. When interoperating, import a CommonJS package from ESM rather than mixing syntax in one file.
Verification
Run the file again and confirm it executes, then run the broader test suite and any CLI entry points to confirm nothing else assumed the old module type.
Diagnostic commands
Check the behavior with commands like these:
node -p "require('./package.json').type" ; ls package.json *.mjs *.cjs
mv file.js file.mjs && node file.mjs
grep -n "require(\|import " file.js