You’re happily writing JavaScript (or TypeScript), you type:
import { something } from './myModule.js'
SyntaxError: Cannot use import statement outside a module
Ugh. What just happened, Why does JavaScript suddenly refuse your clean import
line
I’ll walk you through exactly why that error happens, when and where it applies (browser vs Node.js vs testing), and how to fix it in real projects. I’ll also compare what current blogs say (where they fall short) and share new tips you may not see elsewhere. By the end, you’ll confidently fix this error and avoid it in future.
What the Error
When JavaScript says “Cannot use import statement outside a module”, it’s telling you:
“Hey, I see you used an import
keyword here, but I’m not treating this file (or execution context) as an ES module. So I can’t process import
. I expected a plain script or CommonJS instead.”
In more concrete terms:
- ES modules are JavaScript files where you use
import
/export
. - But not every JS file is automatically a module. Sometimes environments assume “plain script” or CommonJS modules.
- If your file isn’t recognized as a module, JavaScript (in browser or Node) rejects the
import
syntax and raises that error.
Why It Happen in Browsers
Script tags need type="module"
In HTML, when you include JavaScript via <script>
, by default it’s a “regular script.” Regular scripts do not support import
/export
. To tell the browser “Hey, this is a module,” you use:
<script type="module" src="main.js"></script>
Without type="module"
, the browser treats it as plain JS and will throw the error.
Server and MIME type
Even if you set type="module"
, your server must serve the .js
file with the right MIME type (text/javascript
). Some servers mistakenly send wrong types (like text/plain
), which breaks module loading.
Relative paths, file extensions
Imports in modules must often include the correct path and extension. For example:
import { foo } from './module.js'
If you wrote import { foo } from './module'
(without .js
) or used wrong relative path, the browser may fail to resolve or treat it incorrectly. Many how-to blogs mention this briefly but don’t stress the “always include extension” rule. (I’ll show a deeper example later.)
No bundler means module isolation
In large apps, you usually bundle your modules (Webpack, Rollup, etc.). Bundlers understand your import
statements and combine files into something the browser can load. If you skip bundling or misconfigure it, your raw module code might reach the browser untransformed, triggering this error.
Why It Happen in Node.js
Node.js has its own “default module” rules, and things get confusing because Node historically used CommonJS (with require
) and now supports ES modules side by side. Many blogs cover the basics (package "type"
or .mjs
), but I’ll go deeper, including tricky cases.
CommonJS vs. ES Module in Node
- By default, Node treats
.js
files as CommonJS, which usesrequire
,module.exports
, etc. - ES modules (
import
/export
) need special enabling. - Node recognizes
.mjs
files as ES modules automatically (no special config). - Or, you can set
"type": "module"
in yourpackage.json
, telling Node to interpret.js
files as ES modules.
So, if you write import
in a .js
in a project where Node assumes CommonJS, you’ll get the “Cannot use import statement outside a module” error.
Mix ES and CommonJS in One Project
Sometimes you want to mix both. E.g., some third-party library is CommonJS, or you have legacy code. In that case:
- Use
.cjs
for files intended as CommonJS. - Use
.mjs
for ES modules explicitly. - Or use
import()
dynamic import to import ES modules from CommonJS context (or vice versa). - Or use Babel or a transpiler to convert ES modules into CommonJS.
Node version and experimental modules
Older Node versions (before v13.2.0) required using the --experimental-modules
flag to enable ES module support. Newer Node versions have better built-in support. If you’re stuck on an old version, that flag might be needed.
Using type: module
in package.json
Example:
{
"name": "my-app",
"version": "1.0.0",
"type": "module"
}
With that, Node treats .js
files as ES modules, so import
works. But remember: then any file using CommonJS (require
) must instead use .cjs
extension or fallback.
Other Cases Testing, Bundlers, Mixed Code
Jest / Testing Environments
If you write tests and use import
, Jest might fail with the same “Cannot use import statement outside a module” unless configured to handle ES modules. You might need:
- Use
babel-jest
to transpileimport
→require
. - Add
"type": "module"
inpackage.json
used in the test environment. - Configure Jest’s
transform
ortransformIgnorePatterns
so that your ES modules (or node_modules using ES modules) get transpiled.
Many blog posts skip this section, but in real projects this is often where the error surfaces (in your tests).
Bundlers and Transpilers
If you’re using Webpack, Rollup, Babel, or similar, you must ensure your bundling pipeline understands ES modules:
- Babel preset
@babel/preset-env
must be configured to allow module syntax or transform to target environment modules. - Webpack must not accidentally treat your module as “external” or ignore it.
- For library authors, bundling target matters (UMD, ESM, CJS).
- If a third-party library ships in ES module form and your bundler isn’t set to handle it, you might see this error.
Dynamic Import
Sometimes, you can avoid the static import
by using import()
dynamically, especially in mixed module environments or lazy loading. This may bypass the “outside module” check because import()
is treated differently in some runtimes.
Fixes The Code
Now let’s get hands dirty. Below are concrete steps and example code to fix the error depending on your scenario.
In Browser / Frontend Projects
- Use
type="module"
on your script tags:
<script type="module" src="index.js"></script>
- Always use correct paths and extensions:
// Good
import { greet } from './greet.js';
// Bad (might break)
import { greet } from './greet';
- Use an HTTP server (don’t just open
.html
file viafile://
) because module import may be blocked by CORS or not allowed locally. - If your browser bundle is using older bundlers, ensure they handle ES modules. Use tools like Webpack, Rollup, or Parcel, and configure properly.
In Node.js Projects
- Decide: use ES modules or CommonJS consistently.
- If ES module:
- Set
"type": "module"
inpackage.json
, or - Rename your module files to
.mjs
.
- Set
- If using CommonJS:
- Replace
import
withrequire
:const { foo } = require('./foo.js');
- Use
module.exports
instead ofexport
.
- Replace
- If you have a mix, e.g. one file is ES module and another is CommonJS:
- Name them
.mjs
and.cjs
appropriately, or - Use dynamic
import()
in CommonJS file (which returns a Promise). - Or use a transpiler (Babel) to unify.
- Name them
- For older Node versions, run your script with:
node --experimental-modules yourFile.mjs
In Testing (Jest etc.)
- Install Babel-related packages:
npm install --save-dev babel-jest @babel/core @babel/preset-env
- Create
babel.config.js
:
module.exports = {
presets: [
['@babel/preset-env', { targets: { node: 'current' } }],
],
};
- In Jest config (e.g.
jest.config.js
), set:
module.exports = {
transform: {
'^.+\\.jsx?$': 'babel-jest'
},
// if needed:
transformIgnorePatterns: ['/node_modules/(?!(some-esm-lib)/)']
};
- Optionally, set
"type": "module"
inpackage.json
used in test environment.
Summary
The “Cannot Use Import Statement Outside a Module” error is a sign that your JavaScript file isn’t being treated as an ES module. The fix always involves aligning your environment (browser, Node, bundler, testing) so it knows to use ES modules where import
/export
is allowed.