Import and export Classes and Functions in JavaScript

avatar
Borislav Hadzhiev

Last updated: Mar 6, 2024
10 min

banner

# Table of Contents

  1. Import a Class from another file using JavaScript
  2. Import Functions from another file using JavaScript
  3. Import Variables from another file using JavaScript
  4. Re-export values from another file in JavaScript

# Import a Class from another file using JavaScript

To import a class from another file in JavaScript:

  1. Export the class from file A, e.g. export class Employee {}.
  2. Import the class in file B as import {Employee} from './another-file.js'.
  3. Instantiate the class in file B.

Here is an example of exporting a class from a file called another-file.js.

another-file.js
// ๐Ÿ‘‡๏ธ named export export class Employee { constructor(name, salary) { this.name = name; this.salary = salary; } increaseSalary() { this.salary += 100; } }
The code for this article is available on GitHub

Here is how we would import the Employee class in a file called index.js.

index.js
// ๐Ÿ‘‡๏ธ named import import {Employee} from './another-file.js'; const employee = new Employee('Alice', 100); console.log(employee.name); // ๐Ÿ‘‰๏ธ 'Alice' console.log(employee.salary); // ๐Ÿ‘‰๏ธ 100 employee.increaseSalary(); console.log(employee.salary); // ๐Ÿ‘‰๏ธ 200

import class from another file

The code for this article is available on GitHub

Make sure to correct the path that points to the another-file.js module if you have to.

The example assumes that another-file.js and index.js are located in the same directory.

For example, if another-file.js was located one directory up, you'd have to import as import {Employee} from '../another-file.js'.

We wrapped the name of the class in curly braces when importing it. This is called a named import.

The import/export syntax is called JavaScript modules.

In order to be able to import a class from a different file, it has to be exported using a named or a default export.

The example above uses a named export and a named import.

The main difference between named and default exports and imports is that you can have multiple named exports per file, but you can only have a single default export.

# Using default export and default import

Let's look at an example of how we would import a class that was exported using a default export.

Here are the contents of another-file.js.

another-file.js
// ๐Ÿ‘‡๏ธ default export export default class Employee { constructor(name, salary) { this.name = name; this.salary = salary; } increaseSalary() { this.salary += 100; } }
The code for this article is available on GitHub

IMPORTANT: If you are exporting a variable (or an arrow function) as a default export, you have to declare it on 1 line and export it on the next.

You can't declare and default export a variable on the same line.

And here is how we would import the class using a default import.

index.js
// ๐Ÿ‘‡๏ธ default import import Employee from './another-file.js'; const employee = new Employee('Alice', 100); console.log(employee.name); // ๐Ÿ‘‰๏ธ 'Alice' console.log(employee.salary); // ๐Ÿ‘‰๏ธ 100 employee.increaseSalary(); console.log(employee.salary); // ๐Ÿ‘‰๏ธ 200

using default export and default import

The code for this article is available on GitHub

Notice that we didn't wrap the import in curly braces.

We could have also used a different name when importing the class, e.g. FooBar.

index.js
// ๐Ÿ‘‡๏ธ default import import FooBar from './another-file.js'; const employee = new FooBar('Alice', 100); console.log(employee.name); // ๐Ÿ‘‰๏ธ 'Alice' console.log(employee.salary); // ๐Ÿ‘‰๏ธ 100 employee.increaseSalary(); console.log(employee.salary); // ๐Ÿ‘‰๏ธ 200
The code for this article is available on GitHub

This works but is confusing and should be avoided.

In my experience, most real-world codebases exclusively use named exports and imports, because they make it easier to leverage your IDE for auto-completion and auto-imports.

You also don't have to think about which members are exported with default or named export.

# Using both named and default exports

You can also mix and match. Here is an example of a file that uses both a default and a named export.

another-file.js
// ๐Ÿ‘‡๏ธ default export export default class Employee { constructor(name, salary) { this.name = name; this.salary = salary; } increaseSalary() { this.salary += 100; } } // ๐Ÿ‘‡๏ธ named export export class Person { constructor(name, age) { this.name = name; this.age = age; } increaseAge() { this.age += 1; } }
The code for this article is available on GitHub

And here is how you would import the two classes.

index.js
// ๐Ÿ‘‡๏ธ default and named imports import Employee, {Person} from './another-file.js'; const employee = new Employee('Alice', 100); console.log(employee.name); // ๐Ÿ‘‰๏ธ 'Alice' console.log(employee.salary); // ๐Ÿ‘‰๏ธ 100 employee.increaseSalary(); console.log(employee.salary); // ๐Ÿ‘‰๏ธ 200 const person = new Person('Bob', 30); person.increaseAge(); console.log(person.age); // ๐Ÿ‘‰๏ธ 31

using both named and default exports

The code for this article is available on GitHub

We used a default import to import the Employee class and a named import to import the Person class.

Note that you can only have a single default export per file, but you can have as many named exports as necessary.

# Import Functions from another file using JavaScript

To import a function from another file in JavaScript:

  1. Export the function from file A, e.g. export function sum() {}.
  2. Import the function in file B as import {sum} from './another-file.js'.
  3. Use the imported function in file B.

Here is an example of exporting functions from a file called another-file.js.

another-file.js
// ๐Ÿ‘‡๏ธ named export export function sum(a, b) { return a + b; } // ๐Ÿ‘‡๏ธ named export export function multiply(a, b) { return a * b; } // (arrow function) // export const sum = (a, b) => { // return a + b; // };
The code for this article is available on GitHub

The syntax is the same when using arrow functions. All you have to do is use the export keyword.

Here is how we would import the functions in a file called index.js.

index.js
// ๐Ÿ‘‡๏ธ named import import {sum, multiply} from './another-file.js'; console.log(sum(35, 65)); // ๐Ÿ‘‰๏ธ 100 console.log(multiply(5, 5)); // ๐Ÿ‘‰๏ธ 25
The code for this article is available on GitHub

Make sure to correct the path that points to the another-file.js module if you have to.

The example assumes that another-file.js and index.js are located in the same directory.

For example, if another-file.js was located one directory up, you'd have to import as import {sum} from '../another-file.js'.

We wrapped the name of the function in curly braces when importing it. This is called a named import.

The import/export syntax is called JavaScript modules.

In order to be able to import a function from a different file, it has to be exported using a named or default export.

The example above uses a named export and a named import.

The main difference between named and default exports and imports is that you can have multiple named exports per file, but you can only have a single default export.

# Using a default export and a default import

Let's look at an example of how we would import a function that was exported using a default export.

Here are the contents of another-file.js.

another-file.js
// ๐Ÿ‘‡๏ธ default export export default function sum(a, b) { return a + b; } // (arrow function) // const sum = (a, b) => { // return a + b; // }; // export default sum;
The code for this article is available on GitHub

And here is how we would import the function using a default import.

index.js
// ๐Ÿ‘‡๏ธ default import import sum from './another-file.js'; console.log(sum(35, 65)); // ๐Ÿ‘‰๏ธ 100
The code for this article is available on GitHub

Notice that we didn't wrap the import in curly braces.

We could have also used a different name when importing the function, e.g. foo.

index.js
// ๐Ÿ‘‡๏ธ default import import foo from './another-file.js'; console.log(foo(35, 65)); // ๐Ÿ‘‰๏ธ 100

This works but is confusing and should be avoided.

IMPORTANT: If you are exporting a variable (or an arrow function) as a default export, you have to declare it on 1 line and export it on the next.

You can't declare and default export a variable on the same line.

another-file.js
const sum = (a, b) => { return a + b; }; // ๐Ÿ‘‡๏ธ default export export default sum;
In my experience, most real-world codebases exclusively use named exports and imports because they make it easier to leverage your IDE for auto-completion and auto-imports.

You also don't have to think about which members are exported with default or named export.

# Using default and named exports and imports

You can also mix and match, here is an example of a file that uses both a default and a named export.

another-file.js
// ๐Ÿ‘‡๏ธ default export export default function sum(a, b) { return a + b; } // ๐Ÿ‘‡๏ธ named export export const multiply = (a, b) => { return a * b; };
The code for this article is available on GitHub

And here is how you would import the two functions.

index.js
// ๐Ÿ‘‡๏ธ default and named imports import sum, {multiply} from './another-file.js'; console.log(sum(35, 65)); // ๐Ÿ‘‰๏ธ 100 console.log(multiply(10, 10)); // ๐Ÿ‘‰๏ธ 100
The code for this article is available on GitHub

We used a default import to import the sum function and a named import to import the multiply function.

Note that you can only have a single default export per file, but you can have as many named exports as necessary.

# Import Variables from another file using JavaScript

To import a variable from another file in JavaScript:

  1. Export the variable from file A, e.g. export const str = 'Hello world'.
  2. Import the variable in file B as import { str } from './another-file.js'.

Here is an example of exporting two variables from a file called another-file.js.

another-file.js
// ๐Ÿ‘‡๏ธ named export export const str = 'Hello world'; // ๐Ÿ‘‡๏ธ named export export const str2 = 'one two three';

Here is how we would import the variables in a file called index.js.

index.js
// ๐Ÿ‘‡๏ธ named import import {str, str2} from './another-file.js'; console.log(str); // ๐Ÿ‘‰๏ธ "Hello world" console.log(str2); // ๐Ÿ‘‰๏ธ "one two three"

Make sure to correct the path that points to the another-file.js module if you have to. The example assumes that another-file.js and index.js are located in the same directory.

For example, if another-file.js was located one directory up, you'd have to import as import {str} from '../another-file.js'.

We wrapped the name of the variable in curly braces when importing it. This is called a named import.

The syntax we're using to export and import variables is called JavaScript modules.

In order to be able to import a variable from a different file, it has to be exported using a named or a default export.

The example above uses a named export and a named import.

The main difference between named and default exports and imports is that you can have multiple named exports per file, but you can only have a single default export.

# Using a default export and a default import

Let's look at an example of how we would import a variable that was exported using a default export.

Here are the contents of another-file.js.

another-file.js
const str = 'Hello world'; // ๐Ÿ‘‡๏ธ default export export default str;
The code for this article is available on GitHub

And here is how we would import the variable using a default import.

index.js
// ๐Ÿ‘‡๏ธ default import import str from './another-file.js'; console.log(str); // ๐Ÿ‘‰๏ธ "Hello world"

Notice that we didn't wrap the import in curly braces.

We could have also used a different name when importing the variable, e.g. fooBar.

index.js
// ๐Ÿ‘‡๏ธ default import import fooBar from './another-file.js'; console.log(fooBar); // ๐Ÿ‘‰๏ธ "Hello world"
The code for this article is available on GitHub

This works but is confusing and should be avoided.

If you are exporting a variable (or an arrow function) as a default export, you have to declare it on 1 line and export it on the next. You can't declare and default export a variable on the same line.

In my experience, most real-world codebases exclusively use named exports and imports, because they make it easier to leverage your IDE for auto-completion and auto-imports.

You also don't have to think about which members are exported with default or named export.

# Using default and named exports and imports

You can also mix and match. Here is an example of a file that uses both a default and a named export.

another-file.js
const str = 'Hello world'; // ๐Ÿ‘‡๏ธ default export export default str; // ๐Ÿ‘‡๏ธ named export export const num = 100;
The code for this article is available on GitHub

And here is how you would import the two variables.

index.js
// ๐Ÿ‘‡๏ธ default and named imports import str, { num } from './another-file.js'; console.log(str); // ๐Ÿ‘‰๏ธ "Hello world" console.log(num); // ๐Ÿ‘‰๏ธ 100
The code for this article is available on GitHub

We used a default import to import the str variable and a named import to import the num variable.

Note that you can only have a single default export per file, but you can have as many named exports as necessary.

# Re-export values from another file in JavaScript

To re-export values from another file in JavaScript, make sure to export the name exports as export {myFunction, myConstant} from './another-file.js and the default export as export {default} from './another-file.js'.

The values can be imported from the file that re-exported them.

Here is an example of a file that has 2 named exports and a default export.

another-file.js
// ๐Ÿ‘‡๏ธ named export export function increaseSalary(salary) { return salary + 100; } // ๐Ÿ‘‡๏ธ named export export const department = 'accounting'; // ๐Ÿ‘‡๏ธ default export export default function multiply(a, b) { return a * b; }
The code for this article is available on GitHub

Here is how you would re-export the exported members of another-file.js from a file called index.js.

index.js
export {increaseSalary, department, default} from './another-file.js';
The code for this article is available on GitHub

The example above directly re-exports the 2 named exports and the default export.

This means that you can't use increaseSalary, department and the default export in the index.js file because we haven't imported them, we directly re-exported them.

If you have to use the values in the file, you would also have to import them.

index.js
// ๐Ÿ‘‡๏ธ import (only if you need to use them in index.js) import multiply, {increaseSalary, department} from './another-file.js'; // ๐Ÿ‘‡๏ธ re-export export {increaseSalary, department, default} from './another-file.js'; console.log(multiply(5, 5)); // ๐Ÿ‘‰๏ธ 25 console.log(department); // "accounting" console.log(increaseSalary(100)); // ๐Ÿ‘‰๏ธ 200

The syntax for re-exporting members of another module is:

index.js
// ๐Ÿ‘‡๏ธ re-export NAMED exports export {increaseSalary, department} from './another-file.js' // ๐Ÿ‘‡๏ธ re-export DEFAULT export export {default} from './another-file.js'

The two lines from the example above can be combined into a single line if you're re-exporting members of the same file.

If you are re-exporting members from multiple files, you would have to use a line of code for each file.
index.js
// ๐Ÿ‘‡๏ธ re-export NAMED exports export {increaseSalary, department} from './first-file.js' // ๐Ÿ‘‡๏ธ re-export default export export {default} from './second-file.js'

You could then import the re-exported members from the same module.

consumer-file.js
// ๐Ÿ‘‡๏ธ default and named imports (all from index.js) import multiply, {increaseSalary, department} from './index.js'; console.log(multiply(10, 10)); // ๐Ÿ‘‰๏ธ 100 console.log(increaseSalary(100)); // ๐Ÿ‘‰๏ธ 200 console.log(department); // ๐Ÿ‘‰๏ธ "accounting"
The code for this article is available on GitHub
This is the main advantage of re-exporting values - you can re-export the members of multiple files from a single module, which would simplify your imports.

The pattern you often see is - re-export members of different files from a file called index.js. The name index.js is important because you don't have to explicitly specify the name index when importing (in some environments).

However, this pattern is implicit and sometimes frowned upon. For example, in Node.js, the default mode is explicit, which means, you have to import from'./index.js' and are not allowed to import from './.

In general, it's better to be explicit and import from './utils/index.js', rather than ./utils because this syntax is supported in more environments and is more obvious and direct.

Many of the files you use might make use of multiple utility functions that have been extracted into separate files, and you might not want to have 5 lines of imports just for utility functions or constants - this is when re-exporting from an index.js file comes in.

# Additional Resources

You can learn more about the related topics by checking out the following tutorials:

I wrote a book in which I share everything I know about how to become a better, more efficient programmer.
book cover
You can use the search field on my Home Page to filter through all of my articles.

Copyright ยฉ 2024 Borislav Hadzhiev