Have you ever started your Node.js app, only to see an error like SyntaxError: Cannot use import statement outside a module your heart sinks. You thought you were doing everything rightusing import, clean code, modular design. But instead you’re stuck. Don’t worry it’s not a mystical bug, and yes, you can fix it (and understand why it happened).
What on Behind the Scenes
Node.js supports two (or more) module systems, and your file is being treated as not a module that allows import statements. Specifically:
- The
import/exportsyntax comes from ES Modules (ESM). - The older Node.js default (and still common) system is CommonJS, using
require()andmodule.exports.
When you mix these systems without telling Node what you’re doing, you get this error. - The key thing: Node needs to know your files are ESM (or you’re using a compatible syntax) before you can use
import. If it doesn’t, Node says “hey, you’re not in a module context so I won’t let you useimport”.
Fix it in a Node.js Project
Here I walk you through a typical Node.js project, how to decide what module system you want, and how to implement it.
Decide your module system
When you start your project (or inherit one), you should ask:
- Is the project using mostly
require()andmodule.exportstoday? If yes → CommonJS might be simpler. - Do you want to use
import/exportsyntax across your codebase? If yes → go with ES Modules (ESM).
Sticking to one system reduces confusion. Mixingrequire()andimportin the same file usually invites trouble. (Some posts mention this but don’t emphasise decision-making.)
If you choose CommonJS (stay with require)
If you’re staying with CommonJS:
- Use
require()andmodule.exports. - Make sure not to write
import/exportin your files (or if you do, you’ll have to use a transpiler). - Ensure your
package.jsondoes not specify"type": "module"(or explicitly uses"type": "commonjs"in Node versions that support it). - File extensions:
.jsis fine by default.
If you see the “Cannot use import statement outside a module” error, likely you attemptedimportin a file Node treats as CommonJS. Fix it by converting torequire()or by migrating to ESM.
If you choose ES Modules (use import/export)
If you want to use import and export, here’s how to set up your Node.js project:
- In your
package.json(root of project) add:
{
"type": "module",
...
}
- This tells Node: treat
.jsfiles as ESM by default. - If you want to use
.jsfiles as CommonJS in a mixed setup, you can add"type": "module"and use.cjsextension for CommonJS files, or leave.jsas CommonJS and use.mjsfor ESM. Many posts mention this but don’t show examples. For instance:utils.mjs→ ESMlegacy.cjs→ CommonJS
- Rename files if needed: use
.mjsextension for modules if you don’t want to change package.json or if you have older setup. - Ensure Node version supports ESM (prefer Node 14+; ESM got more stable in Node 16+).
- Use
import { something } from './module.js';(note the./module.jsincludes extension unless using package resolution). - No mixing of
require()andimportin the same file unless you handle dynamic imports or carefully manage interoperability. - If using older modules (CommonJS only) inside ESM files, you can use dynamic import:
const mod = await import('./commonjs-module.cjs');
- or use the
createRequirehelper:
import { createRequire } from 'module';
const require = createRequire(import.meta.url);
const oldMod = require('./oldFile.js');
Debugging the “Cannot use import statement outside a module” Error
When you still hit the error despite your setup, try these checks:
- Open a terminal and run
node -v. Make sure you’re on a supported version. - In your
package.json, check the"type"field: if it’s missing or set to"commonjs", Node treats files as CommonJS by default. If you wroteimport, that’s a mismatch. - Check file extension. If you’re using
.js, Node might treat it as CommonJS unless"type":"module"is set. - In your code file, check for ambiguity: Are you using
importbut alsorequire()? Are you mixing syntaxes? - Look at the error trace: it usually points exactly to the
importline and says “outside a module”, telling you Node considered that file not to be an ES module. - If using a test framework (e.g., Jest), check its configuration: it may default to CommonJS and will choke on
importunless configured. - If using a bundler or transpiler, check that the build step preserves module syntax or transforms correctly (some bundlers by default transpile modules to CommonJS, which may confuse Node).
Migrating a small Node.js project to ESM
Let’s say you have a small project with files: index.js, utils.js. You want to use import syntax going forward. Steps:
In package.json, add "type": "module".
Update utils.js:
// utils.js
export function greet(name) {
return `Hello, ${name}!`;
}
Update index.js:
import { greet } from './utils.js';
console.log(greet('World'));
Edge Cases and Bonus Tips
- Using
.mjsvs.js: You can skip"type": "module"by naming ESM files.mjs. That ensures Node treats them as modules. Some blog posts mention this, but don’t show when that’s useful (e.g., mixed codebase or incremental migration). - Mixed modules in same app: If you have a legacy codebase with many
require()uses, you might choose to keep CommonJS for now and gradually migrate to ESM. You can rename new modules to.mjs, or keep.jswith"type": "module"and convert legacy files to.cjs. Having both systems is possible but adds complexity. - Transpilers and bundlers: If you use a tool like Babel or Webpack, they might transform
importtorequireor vice versa. That means you could write ESM syntax but end up with CommonJS code at runtime. If Node then tries to interpret the file as ESM (because of"type": "module"), you might still hit issues. So, check your build step and output file type. - Testing frameworks: As mentioned earlier, Jest for example may default to CommonJS. You’ll need to configure it to handle ESM (for example via
babel-jestor by setting"type": "module"and using.mjsfiles) else you’ll get the same error during tests. - Imports from
node_modules: Some modules innode_modulesare published as ESM (import/export) and others as CommonJS (require/exports). If you import an ESM module from a CommonJS file, Node might complain. One solution: switch your file to ESM or use dynamic import as described. A Reddit user described this: “It looks like the library you’re trying to use is using ES6 modules… You could hook up Babel … but probably easier would be to simply write your code using ES6 and then tell Node to run it as ES6 by addingtype: "module".” - Version compatibility: Older Node.js versions (before ~12.x) had experimental support for ESM and required flags like
--experimental-modules. Modern Node (14+, 16+) has stable ESM support. If you’re on an older version you might need that flag. - File path & extension quirks: When using ESM you often need to include file extensions in imports (e.g.,
import { x } from './file.js';). Omitting.jscan cause mysterious errors. This is a nuance many posts gloss over. - Dynamic
import(): In mixed environments you can useawait import()(in async context) to load modules dynamically. This works in ESM contexts and can help when you need to load a module at runtime that doesn’t map cleanly into either system.
Fix Decision Matrix
| Your situation | What to do |
|---|---|
New project, you want import/export syntax | Use ESM: set "type": "module" in package.json, use .js or .mjs, no mixing |
Legacy project with many require(), don’t want big changes now | Stay CommonJS: use require(), ensure "type" is not set to "module", convert gradually |
| Mixed modules (some old, some new) | Use .cjs for old CommonJS, .mjs for new ESM; or set "type": "module" and convert old files carefully; use dynamic import for tricky cases |
| Unexpected “Cannot use import statement outside a module” error | Debug: check Node version, check "type" in package.json, check file extension, ensure correct syntax and file path, check test/bundler config |
Final Thoughts
The error SyntaxError: Cannot use import statement outside a module may look scary, but it’s really a signal telling you: “Node is treating this file as non-module (CommonJS) but you used ES module syntax (import)”. Once you understand the distinction between CommonJS and ES Modules, and how Node decides which one you’re using (via package.json "type" or file extensions like .mjs/.cjs), it becomes straightforward.
