
None will rival software developers’ frustrations more than when they write neat, modern code, hit the run button, and are instead greeted by a wall of red text. They expected the script to run without any problem, but their script turned out to be specific and stubborn. The terminal gave “Uncaught SyntaxError: Cannot use import statement outside a module” as the error.
This error has become something of a Japanese rite for JavaScript coders. It exists and is persistent, warns: it is usually what happens when you update a project from an older standard or simply copy-pasting a tutorial snippet in your local environment. It visually seems like a syntax problem that is way too complicated, but it is actually a configuration disagreement. You wrote your code in a state-of-the-art language (ES Modules), but your host environment (Node.js) is reading your code as an older format (CommonJS).
Luckily, this problem can be fixed. There is no need to change your entire codebase or abandon modern syntax. This article has four different and effective solutions to the “Cannot use import statement error,” ranging from minor configuration changes to advanced build setups.
What does the error mean: why is it happening?
If you want to fix the problem correctly, you must first understand the problem’s nature. When you encounter the “Cannot use import statement outside a module” message, Node.js is indicating that it cannot figure out how to deal with the import keyword in the file it is currently processing.
A confrontation of two module systems
Originally, JavaScript lacked a native import notation to fetch code from other files and integrate them into one. Hence, Node.js adopted CommonJS to fill in that gap. This system has the require() function for loading dependencies and module.exports for sharing codes.
However, 2015 marked a new chapter in the standardization process of JavaScript modules, with the release of ECMAScript modules (ESM). The introduction of import and export statements meant that modules could be seamlessly used across both server and client environments.
Now the trouble is that Node.js continues to default to CommonJS for backward compatibility purposes. When you write a normal .js file and put an import statement inside it, Node.js mistakenly treats the file as CommonJS and tries to parse it the wrong way. CommonJS simply doesn’t understand the import keyword, hence the system fails.
Showing the module difference
The following snippet is a handy reference of what the two are like:
CommonJS (What Node expects by default):
const express = require(‘express’);
const app = express();
module.exports = app;
ES Modules (What you are likely writing):
import express from ‘express’;
const app = express();
export default app;
If you run the second example in a regular Node.js setup, you will get the “Cannot use import statement outside a module” error.
Typical cases for the occurrence of the error
This nodejs import error is commonly triggered in situations such as:
- Frontend to Backend Transitions: If you have been mainly coding with React, Vue, or Angular (which hide the detail of import through bundlers) but try to write backend Node code from the same angle, you will face this problem.
- Copy-Pasting Tutorials: You see a modern tutorial that uses the ES6 syntax, take it, and paste it into your old project setup that does not support the features, and hence you get the error.
- New npm packages: An important statistic that developers should take into account is that progressively most open-source packages are heading towards the “Pure ESM” direction, meaning that they only support import statements which force the developers to upgrade their environment accordingly.
Prepare
Before deciding on one of the workarounds, you should make sure your setup is ready for modern JavaScript. Although ES Module support is now widely available, some older Node.js versions may require you to use experimental flags or won’t be able to handle them at all.
Verify Node version
Node.js 12 was the version where native ESM was initially stabilized, and version 14 marked its maturity. If the Node version installed on your machine is outdated, the below-mentioned configuration changes will not help you.
To find out your version by opening a terminal and running:
node -v
It would be best to update to the latest version if it is less than 14.0.0. The newest LTS release is always the most stable and secure, making it the best choice practically. Once your build environment is modern, you can instruct Node.js to consider that modules are ESM rather than CommonJS.
Method 1: The “Type: Module” Fix (Highly recommended)
For the majority of modern projects, the solution presented here is the most direct and elegant one. It entails changing one line of your project’s configuration file, package.json.
The package.json
The package.json serves as an identity card of your project: it lists dependencies, script commands, version numbers, and so on. But more importantly, it conveys to Node.js the way that it should interpret the JavaScript files inside the project. In other words, you can use a simple property change in this file to switch the whole project from CommonJS to ES modules.
How to implement it
- Find package.json: The root of your project’s directory is where you will usually find package.json. If you are missing it, don’t worry, just run npm init -y from your terminal to generate one.
- Insert the “type” attribute: At the highest level of the JSON object, you must add the key-value pair “type”: “module”.
Here is an example:
{
“name”: “my-app”,
“version”: “1.0.0”,
“type”: “module”,
“scripts”: {
“start”: “node index.js”
},
“dependencies”: {
“express”: “^4.17.1”
}
}
With the addition of “type”: “module”, you have instructed Node.js that all your project’s .js files should be treated as ES Modules which altogether eliminates the “Cannot use import statement outside a module” error for the project.
Troubleshooting and the impact of changes
Yes, it is the best solution for the javascript import problem that we knew of but this solution still has its own hiccups. Once you convert your project folder to the module type, require() is no longer your friend! If you happen to mix the usage of import and require in the same script, the fix for the import error will result in the appearance of another error saying “require is not defined”.
Being consistent is the only way in and out of it. Replace all occurrences of const x = require(‘x’) with import x from ‘x’ This way you keep your code aligned to the latest standards and, at the same time, without any errors.
Method 2: Extension Change (.mjs)
Maybe you don’t want to change settings for the whole project. It’s possible that you are executing a standalone script or that you are working in a legacy-codebase where a package.json change will break other tools. In this situation, the .mjs extension gives you a way out.
.mjs extension – when to use
Node.js follows an internal guideline by which it always interprets files that have the .mjs (Module JavaScript) extension as ES Modules, even if the package.json states otherwise. This makes it possible to have CommonJS files (.js) and ES Module files (.mjs) coexist in a project.
Filename change
You can
- Identify the file that causes the “Cannot use import statement outside a module” error (e.g., script.js)
- Change its name to script.mjs
- Change the way it is referred in other scripts accordingly
Execute the program
Don’t forget to specify the new file extension when you execute the script:
node script.mjs
Compatibility issues
Although this is an effective method, there are some minor disadvantages as far as the developer experience is concerned. It might happen that some Code Editors or IDEs do not recognize .mjs as JavaScript and hence you lose the benefits of syntax highlighting or linting unless you install the necessary plugins or tweak settings. Anyway, if it is just a quick script or single utility file, the method allows you to get rid of the import error super fast without changing the rest of your application.
Method 3: Creating a build with Babel
Developers who wish to write modern code but still want to be able to run their applications in legacy environments (which could be very old versions of Node or certain browsers) are normally in need of a transpiler. Babel is the most famous one among such transpilers.
Babel – the reason behind the use?
Babel enables you to write the absolute latest JavaScript (including import statements) that can then be “transpiled” or changed by it into older JavaScript (using require). This is why you don’t get the “Cannot use import statement outside a module” error anymore because the code executed does not have import statements – Babel removed them.
Babel installation
This solution is quite a bit more elaborate than the two previously mentioned ones. You will actually need to install Babel core, the command-line tool, and a preset that instructs Babel to transform your code in a certain way.
Run the command below in your terminal:
npm install –save-dev @babel/core @babel/cli @babel/preset-env
Babel configuration
After that, you add a .babelrc file in your project’s root directory. By doing this, you point to the preset you want Babel to load.
{
“presets”: [“@babel/preset-env”]
}
Compiler execution
You can compile your program with Babel at this point. Instead of doing node index.js straight away, you call Babel to give you an intermediate version of the same file that you subsequently execute.
To accommodate this scenario, add a script in your package.json:
“scripts”: {
“build”: “babel index.js -o dist/index.js”,
“start”: “node dist/index.js”
}
Babel transforms your ES6 code to CommonJS format in the dist folder by running npm run build. Afterward, Node.js can run the file without any problem. This is an efficient javascript import fix for production environments where consistency across versions is a must.
Method 4: Webpack configuration
Your issues won’t be resolved by just a javascript importfix or Babel if you are developing a sophisticated app especially for the web. What you probably have is lots of images, CSS, and dozens of JavaScript files that work together. Hence, a bundler like Webpack becomes the industry standard.
Type of projects requiring Webpack
Webpack single script is not justified. However, if you make a big project with plenty of dependencies and the “Cannot use import statement outside a module” error is part of the project, then setting up a bundler is indeed the right thing to do. Webpack takes all your files (which use import and export) and bundles them into a single, optimized file that browsers or Node can read without error.
Installation and initial setup
First things first, you
npm install –save-dev webpack webpack-cli
Config creation
A file called webpack.config.js will be made. This file tells Webpack where to start and where to put the final bundle.
const path = require(‘path’);
module.exports = {
entry: ‘./src/index.js’, // Your main file
output: {
filename: ‘bundle.js’, // The output file
path: path.resolve(__dirname, ‘dist’),
},
mode: ‘development’ // or ‘production’
};
Running the bundle
Just give the webpack command a go. It is going to look for all dependencies starting from the entry point, and eventually create a bundle.js file in your output directory.
The Workflow:
- You write code with import.
- Webpack reads your code.
- Webpack resolves modules internally.
- Webpack outputs a single file that does not rely on runtime module loading.
- You run node dist/bundle.js.
Runtime sees only one file, so the nodejs import issue has gone forever. This means the runtime environment won’t have to handle an import statement anymore since Webpack already did so during the build phase.
Conclusion
The transition from CommonJS to ES Modules has been a long road for the JavaScript community, and the “Cannot use import statement outside a module” error is the most common bump in that road. However, it is not a dead end.
We have covered 4 ways to go around this effectively:
- Change package.json: The addition of “type”: “module” is the up-to-date, recommended solution for any new Node.js projects.
- Use .mjs extension: A quick remedy for single scripts that work by skipping the global config.
- Babel: The smart choice if backwards compatibility is your concern that allows you to maintain modern coding without losing the option to run on older JavaScript engines.
- Webpack: The decision for pro-level big app/game frontend development.
By and large, if you are new in the game and about to start a web project in 2024, then the first way shown here would be your best bet. It is in line with the next-gen JavaScript world and gives you full freedom to use the language as you know it. On top of that, you won’t just solve the issue at hand, your work will become more modular, readable, and globally standardized.
If it happens that you are facing the same error even after trying these four approaches or bumping into some peculiar behavior of a third-party library, do post your questions here. The sector of JavaScript is huge and there is always something new to learn.
