519

I am trying to run some ES6 code in my project but I am getting an unexpected token export error.

export class MyClass {
  constructor() {
    console.log("es6");
  }
}
str
  • 42,689
  • 17
  • 109
  • 127
Jason
  • 5,247
  • 2
  • 10
  • 3
  • 6
    there is not enough information about your environment or configuration to offer any assistance. This error is suggesting that either webpack or babel are not working correctly, as `export` is only available in ES6, and those modules are what provide ES6 support. – Claies Jul 10 '16 at 21:44
  • 59
    You should use `module.exports = MyClass`, not `export class MyClass` – onmyway133 Aug 04 '17 at 09:47
  • 1
    If you're running this in Node > 12, rename the file to myFile.mjs. That `mjs` extension should tell Node you're using ES6 Module syntax. – Yair Kukielka Oct 27 '21 at 07:23

19 Answers19

520

Updated for 2022

You are using EcmaScript Module (ESM or 'ES6 Modules') syntax but your environment does not support it.

NodeJS versions prior to v14.13.0 do not support ESM (export keyword syntax) and use CommonJS Modules (module.exports property syntax). NodeJS v14.13.0 and newer supports ESM but it must be enabled first.

Solutions:

  • If you are using NodeJS v14.13.0 or newer (which does support ESM) you can enable it by setting "type":"module" in your project package.json
  • Refactor with CommonJS Module syntax (for older versions of NodeJS)
  • Consider using TypeScript alongside ts-node or ts-node-dev npm packages (for instant transpilation at development time) and write TypeScript in .ts files
  • Transpile ESM to CommonJS using esbuild (esbuild package on npm) configured to transpile your ES6 javascript to a CommonJS target supported by your environment. (babel is no longer recommended)
Phil Ricketts
  • 8,236
  • 3
  • 28
  • 31
  • 51
    When will nodejs support `import` natively? I thought v10.0.0 would have it but apparently not. – chovy Apr 30 '18 at 20:42
  • 8
    @chovy experimental support is available with flag "--experimental-modules". Files needs to have a .mjs extension – Giovanni P. May 06 '18 at 22:44
  • 4
    I'm getting this error using Chrome 66 with native support for modules. – Tom Russell Feb 23 '19 at 06:14
  • BTW: note that while node10/node12 support modules behind the flag, [they don't support it in REPL mode -- however it is supported in eval mode in node12](https://stackoverflow.com/q/54784608/245966). – jakub.g Jul 17 '19 at 13:27
  • > Add "type": "module" to the package.json for your project, and Node.js will treat all .js files in your project as ES modules. - https://medium.com/@nodejs/announcing-a-new-experimental-modules-1be8d2d6c2ff – Brian Burns Aug 01 '19 at 01:55
  • 3
    For someone are still not clear about CommonJs Syntax. please checkout this link, may help a little bit. https://flaviocopes.com/commonjs/ – L.T. Aug 26 '19 at 13:19
  • 28
    Not really a helpful comment, but for someone outside of the front-end world it is all quite confusing. Just wanted to build a module used for web, which I am testing in the CLI. I would've assumed that Node.js being mature environment would support ES6 syntax out of the box. – Tom Raganowicz Apr 04 '20 at 19:53
  • 1
    It's been quite a while, and this answer was true at the time, but many Node versions later is now more misleading than helpful. ESM supported landed several versions before the current LTS version (feature flagged in v12, landed in v14, current LTS is v16). As long as you have `"type": "module"` in your `package.json`, ESM syntax will work fine. It would be a good idea to explicitly state which obsolete version(s) of Node this answer was for, and how it's no longer applicable to current Node. – Mike 'Pomax' Kamermans Jan 28 '22 at 18:07
  • 1
    @Mike'Pomax'Kamermans Just seen your comment, I've updated the answer accordingly. Things move quickly in Javascript land! – Phil Ricketts Mar 15 '22 at 20:39
  • Sometimes they do - ESM took _forever_ to go from draft to landed (and Node still hasn't commited to a true switchover unfortunately). As for transpiling, [esbuild](https://esbuild.github.io) is probably a better suggestion, given how many orders of magnitude faster it is. – Mike 'Pomax' Kamermans Mar 15 '22 at 22:03
  • @Mike'Pomax'Kamermans The `"type": "module"` flag creates more problems than it solves. For example, many of Node's built-in constants (such as `__dirname` and `__filename`) disappear, and have to be manually recreated. It's a lot less work to just use a runtime like `tsx` (which comes with support for `import` and `export`) instead of `node`. – Andrew Koster Jan 06 '23 at 19:33
  • 2
    @AndrewKoster I've just ported a node project to ESM modules, and it was a nightmare. It is a major paradigm shift and spending hours to make it all work is to be expected, especially with various tooling. Definitely not as simple as adding "type":"module" – Phil Ricketts Jan 13 '23 at 20:18
  • I tried "type":"module" in your project package.json but then node wouldn't run – Gerry May 07 '23 at 23:55
372

In case you get this error, it might also be related to how you included the JavaScript file into your html page. When loading modules, you have to explicitly declare those files as such. Here's an example:

//module.js:
function foo(){
   return "foo";
}

var bar = "bar";

export { foo, bar };

When you include the script like this:

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

You will get the error:

Uncaught SyntaxError: Unexpected token export

You need to include the file with a type attribute set to "module":

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

then it should work as expected and you are ready to import your module in another module:

import { foo, bar } from  "./module.js";

console.log( foo() );
console.log( bar );
Pang
  • 9,564
  • 146
  • 81
  • 122
Wilt
  • 41,477
  • 12
  • 152
  • 203
  • 97
    unlike the "most-upvoted" answer, this actually solves the problem and explains why it is happening without suggesting that the only option is to leverage a CommonJS method, APM method, or to transpile our code... This would also be an exception to the w3c standard where `type` is expected to be a valid mime type (aka. media type), so this was an unexpected find. Thanks! – Shaun Wilson May 20 '18 at 00:51
  • 5
    This fixes the error but I then get "Unexpected token {" on the line of the import statement in Chrome 67 with script that is inline eg – PandaWood Jul 27 '18 at 12:01
  • 1
    @PandaWood You have to use ``, when you import from module. I tested it in recent version of Chromium. – Vladimir S. Aug 01 '18 at 07:10
  • I just want to mention when using expert default you are not intended to put the same name, on the other hand export only you must follow same name for function, object ..etc In addition to that, you should import with .js as suffix – 7usien Aug 24 '21 at 18:12
  • How do you use the `import ...` when loading a script from an external site such as jsDelivr? – pir Sep 29 '21 at 00:13
  • The most upvoted answer didn't solve my problem, this one did. – Joris Limonier Dec 30 '21 at 11:39
  • 1
    For anyone reading this, also beware that all `type="module"` will be deferred, which in some cases may mess things up if you were relying on loading order – Kobato Jun 05 '22 at 21:34
  • This also fixed my issue running Ruby on Rails v7. I just had to add the 'module' type like so: `<%= javascript_include_tag "custom/global", media: 'all', type: "module" %>`. That took care of the console error. Thank you. – CWarrington Mar 11 '23 at 03:10
134

My two cents

Export

ES6

myClass.js

export class MyClass1 {
}
export class MyClass2 {
}

other.js

import { MyClass1, MyClass2 } from './myClass';

CommonJS Alternative

myClass.js

class MyClass1 {
}
class MyClass2 {
}
module.exports = { MyClass1, MyClass2 }
// or
// exports = { MyClass1, MyClass2 };

other.js

const { MyClass1, MyClass2 } = require('./myClass');

Export Default

ES6

myClass.js

export default class MyClass {
}

other.js

import MyClass from './myClass';

CommonJS Alternative

myClass.js

module.exports = class MyClass1 {
}

other.js

const MyClass = require('./myClass');
starball
  • 20,030
  • 7
  • 43
  • 238
Barnstokkr
  • 2,904
  • 1
  • 19
  • 34
  • 2
    Thanks! This really helped me understand the difference between ES6 and CommonJS import/export patterns. – camslice Nov 15 '21 at 03:37
  • 1
    Thank you! I tried the above (Export and ES6) with TypeScript (.ts) files in Visual Studio 2022 but, getting "Uncaught SyntaxError: Unexpected token 'export" error. Did anyone else come across this issue? – JGV Jan 26 '23 at 02:42
25

I fixed this by making an entry point file like.

// index.js
require = require('esm')(module)
module.exports = require('./app.js')

and any file I imported inside app.js and beyond worked with imports/exports now you just run it like node index.js

Note: if app.js uses export default, this becomes require('./app.js').default when using the entry point file.

mattdlockyer
  • 6,984
  • 4
  • 40
  • 44
Alex Cory
  • 10,635
  • 10
  • 52
  • 62
23

There is no need to use Babel at this moment (JS has become very powerful) when you can simply use the default JavaScript module exports. Check full tutorial

Message.js

module.exports = 'Hello world';

app.js

var msg = require('./Messages.js');

console.log(msg); // Hello World
Alvin Konda
  • 2,898
  • 1
  • 22
  • 22
12

To use ES6 add babel-preset-env

and in your .babelrc:

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

Answer updated thanks to @ghanbari comment to apply babel 7.

Jalal
  • 3,308
  • 4
  • 35
  • 43
  • 7
    @monsto this question was already tagged `babel` by the author. Whilst Phil Ricketts answer does clarify the problem, which is good, this answer is a direct solution to the author's problem. – boycy Apr 24 '18 at 15:00
  • "@babel/preset-env" – ghanbari Jun 16 '19 at 10:15
7

Install the babel packages @babel/core and @babel/preset which will convert ES6 to a commonjs target as node js doesn't understand ES6 targets directly

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

Then you need to create one configuration file with name .babelrc in your project's root directory and add this code there.

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

YouBee
  • 1,981
  • 1
  • 15
  • 16
  • 2
    I also needed to install @babel/register, otherwise I would still get "SyntaxError: Cannot use import statement outside a module" – molecular Nov 29 '19 at 09:05
  • i'm grateful that someone actually mentioned how you could make it compatible with es6 in this q&a thread! jeez. – Hari Reddy Aug 14 '21 at 22:50
2

I had modules working for a while, and then they weren't with this Uncaught SyntaxError: Unexpected token export error.

Turns out, I had added an open brace without a closed brace. Something like:

if (true) {
/* } missing here */

export function foo() {}

While the big error is the forgetting of the end }, the parser first finds an export inside the brace, which is a no-no.

The export keyword must be in the top level of the file.

So:

if (true) {
    export function foo() {}
}

would also be illegal. When the parser encounters this, it stops parsing right there, announcing the misuse of export vaguely, giving the same error it gives when it is loading a "non-module" JavaScript file that uses the export keyword. It never reports the underlying missing brace error.

Took me a long time to figure that out, so I'm posting here to help any future sufferers.

Ideally, the parser would report that export is only allowed at the top level of the file.

Thomas Andrews
  • 1,577
  • 1
  • 13
  • 31
  • 1
    Nice one. I'm sure this will help someone take a second look at their code. Sometimes the solution is staring us in the face. :) – J.M. Janzen Feb 19 '23 at 22:21
0

I experienced this problem and it took me an hour to find my problem.

The problem was that I was changing my code from non-modules to modules and I forgot to delete the imported script file.

My "table.js" file had the following line. This is the module file.

export function tableize(tableIdentifier) {

My "orderinquiry.js" file had the following line.

import { tableize, changeColumnSizesDynamic } from '../common/table.js';

My "orderinquiry.html" had the following two lines.

<script src='/resources/js/common/table.js'></script>
<script type='module' src='/resources/js/client/orderinquiry.js'></script>

While the second line is good and declares type='module. But the first line directly links to table.js and caused the unexpected error. Everything started working when I removed that first <script>.

bcr666
  • 2,157
  • 1
  • 12
  • 23
  • The error message should be "cannot export in a non-module js." instead of a low-level parsing error "unexpected token export". – zwcloud Nov 18 '22 at 17:18
0

Normally import not working in a .js extension because by default js means cjs version of javascript. If you want to es6 feature you need to rename the .js extension to the .mjs extension

parent.mjs

export default class Car {
   constructor(brand) {
   this.carname = brand;
}
 present() {
   return 'I have a ' + this.carname;
  }
}

child.mjs

import Car from './parent.mjs'
export default class Model extends Car {
constructor(brand, mod , country) {
  super(brand);
  this.model = mod;
  this.country = country;
}
show() {
  return this.present() + ', it is a ' + this.model + "i am from " + 
  this.country;
  }
}

index.html

<!DOCTYPE html>
 <html lang="en" class="no-js">
  <head>
  <meta charset="utf-8">
  <meta http-equiv="x-ua-compatible" content="ie=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0, 
shrink-to-fit=no">
  <title>Quick Start Pack Template</title>
 </head>
 <div class="demo"></div>
 <script type="module">
   import Model from './child.mjs'
   let value = new Model("Ford", "Mustang", "bangladesh")
   document.querySelector(".demo").innerHTML = value.show()
 </script>
 </body>
 </html>

Finally run this code on live server

Razib Hossain
  • 708
  • 5
  • 13
0

Just use tsx as your runtime instead of node. It will allow you to use normal import statements, without switching your project to type: module and without having to deal with the nasty consequences of type: module. In addition, you'll get TypeScript support.

Andrew Koster
  • 1,550
  • 1
  • 21
  • 31
0

Another way to import ES6 module from CommonJS is to rename your ES6 module file to have .mjs extension. This is one way for backward compatibility. Then in your CommonJS code, you can import like this:

Top of file import:

import myClass from "./MyClass.mjs"

or Dynamic Import anywhere in the code:

const myClass = await import("./MyClass.mjs");

Note that your CommonJS file does not need to be renamed to .mjs. More info here.

Antonio Ooi
  • 1,601
  • 1
  • 18
  • 32
0

Got through this problem: browsers only support the ES syntax and do not understand require and exports as used in CommonJS. The problem arises from importing files that have the .ts extension. To solve this use the ES6 module import in your files.

Example: Folder Structure

dist
src
|-app.ts
|-components
  |-button.ts
|-helper
  |-eventHander.ts
tsconfig.json
index.html
  1. In the tsconfig file, set "module": "ES2015"
  2. In the tsconfig file, set "outDir": "./dist"
  3. In the HTML file add the attribute <script type="module" src="./dist/app.js"></script>

You can read more about modules here: JavaScript modules

  • The eventHander.ts file
type Handler<T> = (e: T) => void;
const eventHander: Handler<Event> = (e: Event) => {
  e.preventDefault();
  alert("You Clicked");
};
export default eventHander;
  • In the button.ts
import eventHander from "../helper/eventHander.js"
function createButton(): HTMLElement {
  let button: HTMLElement;
  button = document.createElement("button");
  button.addEventListener("click", eventHander);
  button.innerText = "Click Me"
return button 
}
export default createButton 
  • In the app.ts file
import createButton from "./components/button.js";
const hostElement: HTMLElement = document.getElementById("app")!;
hostElement.insertAdjacentElement("beforeend", createButton());

Your HTML code should be like this:

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="app"></div>
</body>
<script type="module" src="./dist/app.js"></script>
-1

I got the unexpected token export error also when I was trying to import a local javascript module in my project. I solved it by declaring a type as a module when adding a script tag in my index.html file.

<script src = "./path/to/the/module/" type = "module"></script>

-1

In the latest versions of Nodejs (v17?) you can use top-level "import", "async", "await" by using the .mjs file extension - instead of transpiling or workarounds.

   // > node my.mjs
   
   import {MyClass} from 'https://someurl'
   async func () {
     // return some promise
   }
   await func ()
C B
  • 12,482
  • 5
  • 36
  • 48
-1

For those that are viewing this in 2022, I had the same error, however I changed my code to something like this:

    module.exports = () => {
    getUsers: () => users;
    addUser: (user) => users.push(user);
  };
Sebastian
  • 402
  • 3
  • 12
-2

Using ES6 syntax does not work in node, unfortunately, you have to have babel apparently to make the compiler understand syntax such as export or import.

npm install babel-cli --save

Now we need to create a .babelrc file, in the babelrc file, we’ll set babel to use the es2015 preset we installed as its preset when compiling to ES5.

At the root of our app, we’ll create a .babelrc file. $ npm install babel-preset-es2015 --save

At the root of our app, we’ll create a .babelrc file.

{  "presets": ["es2015"] }

Hope it works ... :)

-4

// ✅ Using module.exports instead of export module.exports = { Person, };

-5

I actually want to add simple solution. use constand backticks(`).

const model = `<script type="module" src="/"></<script>`