Understanding JavaScript import and export (ESM syntax)

The import and export statements are one of the most crucial features of JavaScript ES6.

Also known as the EcmaScript Modules (ESM) syntax, it allows you to import and export JavaScript classes, functions, components, constants, any other variables between one JavaScript file and another.

This feature allows you to organize your JavaScript code into small, bite-size files. Making it easier to share, improve, manage, and debug your code as the development phase requires.

This tutorial will help you learn how to export JavaScript modules, and then how you can import those exported modules.

How JavaScript export works

You can use a JavaScript export statement to export any JavaScript variable, function, or class. There is no limit to how many times you can export modules in one JavaScript file:

// example.js
export const user = 'John'

export let days = ['Monday', 'Tuesday', 'Wednesday']

export var countries = ['United States', 'United Kingdom']

export function sayHi() {
  console.log('Hello World!')
}

export const greet = name => {
  console.log('Hello ' + name + '!')
}

export class User {
  constructor(username){
    this.username = username
  }
}

All of these exports are valid.

Multiple exports in a separate line

The example above can be used to export multiple modules, but you can also export modules in a separate line by using the export { .., .., .. } syntax:

const user = 'John'

let days = ['Monday', 'Tuesday', 'Wednesday']

var countries = ['United States', 'United Kingdom']

function sayHi() {
  console.log('Hello World!')
}

const greet = name => {
  console.log('Hello ' + name + '!')
}

class User {
  constructor(username){
    this.username = username
  }
}

export {user, days, countries, sayHi, greet, User}

Notice how user and User could be used in one file without any error. JavaScript is case-sensitive, so it treats them as separate variables.

How JavaScript import works

There are multiple ways to import a module in JavaScript. First, you can import specific modules by using the import { .. } from .. syntax:

import { user, days, countries } from './example'

The from keyword will tell JavaScript which file to look for the modules. Alternatively, you can import code and run it immediately by omitting the variable name and directly importing the file:

import './example'

sayHi();

Since the entire example.js code is executed with the import statement, you can call the sayHi() function immediately after that.

Named export and import variables

You can rename exported variables with the as keyword:

export { greet as greetings, user as myUser }

// you need to import with the new names

import { greetings, myUser } from './example'

And the same can be done with imports:

export { greet, user }

import { greet as greetings, user as myUser } from './example'

Default export and import

When you have only one module to export, you can use the export default statement:

export default class User {
  constructor(username){
    this.username = username
  }
}

// or 
class User {
  constructor(username){
    this.username = username
  }
}

export default User

Unlike regular imports, a default import doesn’t use curly braces {} to wrap its modules:

import User from './example'

Instead of using the default keyword, default import simply write a variable name followed by from statement. Also, the variable name you declared for default import doesn’t need to match the export default variable name:

class User {
  constructor(username){
    this.username = username
  }
}

export default User

// can be imported like this

import usr from './example'

The example above will be understood as import User as usr by JavaScript.

@ symbol in JavaScript import statements

As you code and collaborate with others through GitHub and any other medium, you might find some open source code using @ symbol in their import .. from .. syntax like this:

import Button from '@/components/Button'

// or

import Button from '~/components/Button'

The @ and ~ symbols are custom module loaders that are not part of JavaScript standard specification. Most likely, your project has a configuration file that can understand the symbols when you compile your code (usually through Babel or Webpack).

For example, babel-plugin-root-import treats the symbol as the root path, so you can write @/components instead of ../../components.

Note that this is different from npm scoped packages, which also use @ before the package name

Summary

In JavaScript, you can export modules in three different ways:

  • by placing the export statement before the module: export const .. syntax
  • Or in a separate line: export { a, b, c}
  • You can also specify a default export with export default ..
  • And named export with export {xyz as abc}

Then you can also import these modules as:

  • An import all with import * as x from .. syntax
  • A default import with import x from ..
  • Multiple import with import {x, y, z} from ..
  • Named import with import { x as c } from ..
  • Run import code immediately with import 'x'

JavaScript is case-sensitive, so you might have x is not defined error if you have a mismatch between your import and export statement.

One of the most common example of this is when React library has ReactDOM is not defined error.

And that’s how JavaScript ESM syntax works.

Take your skills to the next level ⚡️

I'm sending out an occasional email with the latest tutorials on programming, web development, and statistics. Drop your email in the box below and I'll send new stuff straight into your inbox!

No spam. Unsubscribe anytime.