The error “cannot use import statement outside a module” primarily occurs in JavaScript, not Python. It happens when you try to use an ES6 import
or export
statement in a file that is not being treated as a module. In JavaScript, modules have their own scope and strict rules for how code is shared. Traditional scripts, however, run in the global scope.
This error can occur in two main contexts: in the browser and in Node.js.
In a browser, a standard <script>
tag treats a file as a classic, global-scope script where import
statements are not valid.
Solution: Add the type="module"
attribute to your script tag.
html
<!-- This will throw the error -->
<script src="my-script.js"></script>
<!-- This will load the script as an ES6 module and allow imports -->
<script type="module" src="my-script.js"></script>
Use code with caution.
In Node.js
In Node.js, the error indicates a mismatch between the module system you are using and how Node.js is configured to interpret your files. By default, Node.js uses CommonJS, which uses require()
instead of import
.
Why This Error Drives Developers Mad
If you write JavaScript long enough, you’ll eventually meet this terrifying wall of red in your console:
Uncaught SyntaxError: Cannot use import statement outside a module
At first, you stare at it. Then you stare at your code. Then you stare at your life choices.
Because — let’s be honest — your code looks correct. You wrote:
import something from "./file.js";
So why is JavaScript acting like it’s never seen an import
before
In this article, I’ll explain exactly why this happens, show you how to fix it in every situation, and most importantly I’ll do it in plain English without making you feel like you need a computer science degree.
I’ll also compare this explanation with the top three articles currently ranking on Google and I’ll beat them by giving you extra fixes they forgot to mention, including offline issues, hybrid app problems, AJAX limitations, Node.js quirks, and Service Worker behavior.
Understand the Root Cause: It’s Not Your Syntax, It’s Your File Type
Before we fix anything, let’s clear up a massive misconception: this error does not mean your import syntax is wrong.
It means JavaScript doesn’t know it’s supposed to treat your file as a module.
In other words, the browser is saying:
“Hey, I see you typed import
, but I’m not in module mode. So I don’t understand that word.”
This happens most commonly in plain <script>
tags like this:
<script src="main.js"></script>
When loaded this way, main.js
is treated as old-fashioned JavaScript, also known as a script file, not a module.
So when you write import something ...
inside it boom, error.
The Correct Way to Load ES6 Modules in the Browser
Browsers don’t magically assume every .js
file is a module. You have to explicitly declare it. Like this:
<script type="module" src="main.js"></script>
That one line immediately tells the browser:
✔ “This file uses modern JavaScript.”
✔ “Please enable import
and export
inside it.”
✔ “Also, load the file in strict mode automatically.”
That’s it. One attribute fixes hours of confusion.
Fix the Error in Node.js (Because Node Is Extra Picky)
If you’re working in Node.js, the browser fix won’t help you. Node treats .js
files as CommonJS modules by default, not ES modules.
So if you write:
import fs from "fs";
Node will throw the same error — unless you choose one of the following fixes:
Rename your file to .mjs
index.mjs
Node will automatically treat .mjs
files as ES Modules.
Add "type": "module"
to package.json
{
"type": "module"
}
Now every .js
file acts like a module.
Use require()
instead of import
const fs = require("fs");
This isn’t modern, but it’s still legal in many Node environments.
Why This Error Is Worse in Hybrid Apps and Offline Scenarios
Now here’s something almost every online tutorial completely ignores:
What if your JavaScript file is cached — for example, inside:
✔ A Progressive Web App (PWA)
✔ An HTML5 AppCache (deprecated but still used in legacy apps)
✔ A Capacitor / Cordova / Ionic hybrid app
“Well, if the file is cached, it should load offline. Right?”
Wrong.
If you try to load it using AJAX or jQuery, like this:
$.ajax({
url: "/scripts/app.js",
dataType: "script",
success: function(data) {
eval(data);
}
});
It will work only when online and fail offline with errors like:
Failed to load resource: net::ERR_FAILED
Even though *the browser *already has the file cached!
Understand Why AJAX Can’t Access AppCache or Service Worker Cache
Browsers only serve cached files to normal <script>
or <link>
tags not to AJAX requests.
So if you rely on jQuery or fetch() to load JavaScript dynamically, it won’t pull from cache in offline mode.
That’s why other articles online fail they never explain how to handle this specific scenario.
But don’t worry I’ve got solutions.
Always Prefer <script>
Tags Over AJAX for Module Loading
Load your scripts like this:
<script type="module" src="/scripts/app.js"></script>
This ensures browser-native caching, offline support, and proper module execution.
If You MUST Load JavaScript Dynamically, Use import()
Not AJAX
ES modules support dynamic import, which works like this:
if (navigator.onLine) {
import('/scripts/app.js').then(module => {
module.init();
});
}
This works more reliably than AJAX + eval, and respects module isolation.
Manually Cache Script Content with localStorage or IndexedDB
If you’re stuck with legacy tech like HTML5 AppCache, you can manually store your script when online, then load from cache when offline.
// Online load
fetch("/scripts/app.js")
.then(res => res.text())
.then(code => {
localStorage.setItem("cachedScript", code);
eval(code);
});
// Offline load
const cached = localStorage.getItem("cachedScript");
if (cached) {
eval(cached);
}
Note: This is a last resort eval() is risky, so only use this when you fully control the script.
Switch from AppCache to Service Workers (The Modern Way)
If you’re still using:
<html manifest="app.appcache">
Then it’s time to let go. AppCache is now technically undead — not quite gone, but definitely unwanted.
Instead, move to Service Workers, which automatically cache resources for both:
✔ <script>
tags
✔ fetch()
requests
A basic service worker script might look like:
self.addEventListener("install", event => {
event.waitUntil(
caches.open("v1").then(cache => {
return cache.addAll([
"/index.html",
"/scripts/app.js"
]);
})
);
});
self.addEventListener("fetch", event => {
event.respondWith(
caches.match(event.request).then(response => {
return response || fetch(event.request);
})
);
});
You don’t need to fully understand this right now — just know:
Service Workers = Modern Offline Magic
This Error Isn’t a Bug It’s a Labeling Issue
Let’s summarize everything in one friendly sentence:
JavaScript isn’t angry at your code it just wants you to clearly declare whether a file is a module or not.
Once you do that using:
<script type="module" src="main.js"></script>
or
"type": "module"
Your imports will behave.
And if you’re dealing with offline environments or hybrid apps, stop using AJAX for script loading, and switch to:
✔ <script type="module">
✔ import()
✔ Service Workers
Final Thought
This error isn’t a sign that your JavaScript is broken it’s just JavaScript asking for a proper introduction. When you clearly tell the browser or Node.js, “Hey, this file is a module,” everything falls into place. Whether you fix it with type="module"
, "type": "module"
in package.json, or a switch to dynamic imports, the solution is always about intent, not syntax. So the next time you see this error, don’t panic just label your file correctly, and JavaScript will happily cooperate.