import in JavaScript: loading code from another file

The import keyword pulls into one file whatever another file exported. Here are its four forms, its rules, and the mistakes that break a module.
4 min read
Believemy logo

A project that grows always outgrows a single file. The day a function written for the shopping cart has to serve the invoicing code, you need a way to share it without copying it: that is exactly what import is for.

It fits on one line, yet follows unusual rules that catch people out: it is read before the file runs, and the name it introduces can never be reassigned.


Definition

import declares that a file needs values made available by another file through export. It creates a local name pointing at the original value.

JAVASCRIPT
// cart.js
export const VAT = 0.2;
export function totalWithTax(net) {
  return net * (1 + VAT);
}

// invoice.js
import { totalWithTax, VAT } from './cart.js';

console.log(totalWithTax(100)); // 120

The quoted path names the source file: starting with ./ or ../, it points at a file in your project; with no dot, it names an installed package, such as import React from 'react'.


The four forms worth knowing

  • Named import with import { a, b } from './x.js': reuses the exported names exactly, inside braces.
  • Default import with import cart from './x.js': picks up the export marked default, and the local name is yours to choose.
  • Namespace import with import * as Cart from './x.js': gathers every export into a single object.
  • Side-effect import with import './styles.css': runs the file without taking anything from it.

Renaming uses as, which saves the day when two files export the same name.

Good to know

Braces are not decoration. import { cart } asks for a named export called cart, while import cart asks for the default export. Mixing them up produces an undefined that is hard to explain.


Three rules behind most breakages

Imports are hoisted to the top of the file. The engine processes them before running a single line, so an imported function works even when called above its own import line. Grouping them at the top is a readability convention.

An import cannot go inside an if or a function. It lives at the top level of the file. To load a module only when it becomes necessary, there is the import() form, which is a function call, returns a Promise and is consumed with await.

An imported name is read-only. It behaves like a const: assigning a new value to it fails. It stays wired to the original variable, though, so if the exporting file changes it, the value you read changes too.


Frequently asked questions

Question

Do I have to write the file extension?

In a browser and in Node, yes: the path is a real address, so ./cart.js rather than ./cart. Build tools such as Vite or Webpack tolerate the omission because they guess the extension for you, which is why code can run in development and break once served as is.


Question

Why is my import undefined?

Three causes cover almost every case: the name inside the braces is not exported under that exact spelling, the capitalization differs by one letter, or the file only offers a default export while you are asking for a named one. Put the import line and the export line side by side and the difference almost always jumps out.


Question

How is it different from require?

require belongs to Node's older module system, loaded at the moment the line runs, whereas import is the language's official system, analyzed before execution. Both still coexist in many projects, and which one applies depends on the package.json file. Our Node.js course covers that split, still one of the most common sources of confusion on the server side.

Related terms

Discover our javaScript glossary

Every word of JavaScript explained simply: keywords, built-in objects, methods, errors and concepts. Clear definitions and examples that actually run, to learn and to troubleshoot.

Share this article

Want to help us? Share this article on your networks or even better: on your site, in an article or in your newsletter.