[Fixed] SyntaxError Cannot Use Import Statement Outside a Module

Step by Step to fix: SyntaxError Cannot Use Import Statement Outside a Module

Feb 5, 2023 | Read time 10 minutes

🔔 Table of contents

Introduction

Ever started a project or followed an online tutorial and everything is going fine until you hit this error:

SyntaxError Cannot Use Import Statement Outside a Module

The error “SyntaxError Cannot Use Import Statement Outside a Module”, is caused by you trying to use a library that is written as ECMAScript modules (ESM).

Ok? So what does this mean exactly?

In JavaScript there are usually 3 ways to use external libraries:

  1. If you are in the browser, you can use external libraries with a <script> tag.

For example, using jquery library can be done as:

  1. Using the import statement as part ECMAScript modules. This is the more modern way of importing libraries and you will see this in more online tutorials.

Consider having a math.js file

  

export const add = (a, b) => a + b;
export const subtract = (a, b) => a - b;

Now if we want to use the math.js function, with ECMAScript modules, we can use the import keyword:

  
import { add, subtract } from './math.js';

console.log(add(1, 2)); // 3
console.log(subtract(5, 3)); // 2
  1. Using the CommonJs require() method. This method is the more older way to managing libraries and was created before agreement on the import statement!

So the error message “SyntaxError Cannot Use Import Statement Outside a Module” just means that you are trying to mix and match the different ways of using a JavaScript library.

There are three ways to fix this issue:

  1. Update your script tag to have the type=module attribute
  2. Update your package.json if your application is a Node application
  3. Convert import statements to CommonJS require() equivalent
  4. Use a transpiler like Babel to convert your import statements

1. Update your script tag to have the type=module attribute

If you are running your JavaScript in the browser, you can change the offending library to become a module like so:

  

<script type="module" src="mymodule.js"></script>

You can also use it as a internal script like:

Tip: Check your file paths

Make sure that you are referencing the correct file path. As an example for relative paths, you MUST have the dot forward slash ("./")

If you import without the ('./') it will not work - eg this will not work: import {a} from "module.js";

2. Update your package.json if your application is a Node application

With the latest version of Node, it uses two types of module loaders - CommonJs and ECMAScript modules. We can tell Node to use ECMAScript modules and our import statements by updating the package.json.

As an example, lets say we are trying to use the cloudinary library like import cloudinary from 'cloudinary'.

However when running the code, we get the following error:

  

  (node:29424) Warning: To load an ES module, set "type": "module" in the package.json or use the .mjs extension.
(Use `node --trace-warnings ...` to show where the warning was created)
c:\Users\...\perspective\cloud.js:1
import cloudinary from 'cloudinary';
^^^^^^

SyntaxError: Cannot use import statement outside a module
    at wrapSafe (node:internal/modules/cjs/loader:1018:16)
    at Module._compile (node:internal/modules/cjs/loader:1066:27)
    at Object.Module._extensions..js (node:internal/modules/cjs/loader:1131:10)
    at Module.load (node:internal/modules/cjs/loader:967:32)
    at Function.Module._load (node:internal/modules/cjs/loader:807:14)
    at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:76:12)
    at node:internal/main/run_main_module:17:47

[Done] exited with code=1 in 0.183 seconds

To fix this, we need to go to our package.json file and add the type:module setting. (Make sure that this setting is on the top level)

  

{
    // ... other package.json stuff
    "type": "module"
    // ... other package.json stuff
}

3. Convert import statements to CommonJS require() equivalent

A common way to fix this issue is to use only one way to load modules. Generally, you cannot mix between CommonJs and ECMAScript modules. Just stick with one way of importing - eg either use require() from CommonJS or import keyword from ES6 modules.

As an example, lets say we are trying to use node-html-parser as a ES6 module with the import keyword:

import { parse } from 'node-html-parser';

Now this can give us the error: SyntaxError Cannot Use Import Statement Outside a Module.

To fix this, we can convert it to use CommonJS with the require() method like so:

const parse = require('node-html-parser');

Tip: Check library support for ES6 modules or CommonJS

In some cases, libraries may be only written in ES6 modules, so you cannot use the CommonJS require() method. The reverse can also occur aswell, the library is only written in CommonJS modules and therefore cannot be used with the import keyword.

In cases like this, consider changing your whole Node application to ES6 modules or wait for the author of the library to support your import method (or createa PR yourself)

4. Use a transpiler like Babel to convert your import statements

One way to make sure that all of code will be able to use ES6 Modules to to use a transpiler like Babel.

To do so we can follow the below steps:

  1. First we’ll install @babel/cli, @babel/core and @babel/preset-env:

npm install --save-dev @babel/cli @babel/core @babel/preset-env

  1. Then we’ll create a .babelrc file for configuring Babel:

touch .babelrc This will host any options we might want to configure Babel with:

{ “presets”: ["@babel/preset-env"] }

We then need to transpile our script first before handing over to Node to run. So on build, we pass the script to babel to transpile it for ES6 and put the code into the dist folder:

In file package.json.

  

"scripts": {
  "build": "babel index.js -d dist"
}

Now, in our “start” commamd, we will run the code from the dist folder intead of the src. So our package.json file will look like:

  

"scripts": {
  "build": "babel index.js -d dist", // replace index.js with your filename
  "start": "npm run build && node dist/index.js"
}

Now let’s start our server:

npm start

Jest error: SyntaxError: Cannot use import statement outside a module

When you are using a testing framework like Jest, you can come across this error also:

  

 FAIL  src/configuration/notifications.test.js
  ● Test suite failed to run

    /var/www/management/node/src/configuration/notifications.test.js:1
    ({"Object.<anonymous>":function(module,exports,require,__dirname,__filename,global,jest){import notifications from './notifications';
                                                                                             ^^^^^^

    SyntaxError: Cannot use import statement outside a module

The reason for this is usually due to Jest not supporting ES modules yet. We can see our code working in Node, but not Jest when testing is due to Node supporting ESM from versions from v16+. However as of the time of writing Jest does not support ESM.

To get around this we have a few options:

  1. Don’t use the esm syntax (import / export) and use CommonJs with the require() method

  2. Ensure you either disable code transforms by passing transform: {} or otherwise configure your transformer to emit ESM rather than the default CommonJS (CJS).

  3. Execute node with –experimental-vm-modules, e.g. node –experimental-vm-modules node_modules/jest/bin/jest.js or NODE_OPTIONS=–experimental-vm-modules npx jest etc..

  4. Consider using babel-jest in your project. (Note: newer versions of Jest should have babel-jest installed already. This applies for older projects/ versions)

Open up your terminal and install babel-jest as follows:

npm install --save-dev babel-jest

Make sure we have updated the package.json:

  

{
  "scripts": {
    "test": "jest"
  },
  "jest": {
    "transform": {
      "^.+\\.[t|j]sx?$": "babel-jest"
    }
  }
}

Create .babelrc configuration file

Create a babel.config.json config in your project root and enable some presets.

To start, you can use the env preset, which enables transforms for ES2015+

npm install @babel/preset-env --save-dev

In your babel.config.json file, enable the presets like this:

  

{
  "presets": ["@babel/preset-env"]
}

Summary

In this article, I went over step by step options to fix the error “SyntaxError Cannot Use Import Statement Outside a Module”. This error is caused by trying to use a library that is using ECMAScript modules (ESM) and we are not importing it correctly.

To fix this issue, we need to first determine where our JavaScript is running. If your JavaScript is in the browser, then update the <script> tag to have type="module". If the code is in the backend with Node - update the package.json to have "type": "module".

The final option is to convert all import keywords to CommonJs require(). Transpilers such as Babel can make this quicker and be part of the build process!

👋 About the Author

G'day! I am Huy a software engineer based in Australia. I have been creating design-centered software for the last 10 years both professionally and as a passion.

My aim to share what I have learnt with you! (and to help me remember 😅)

Follow along on Twitter , GitHub and YouTube