JavaScript Named Exports, Default Exports, Import Aliases, and Dynamic Imports

JavaScript modules help developers organize code into separate files and reuse functions, variables, and classes across a project. The export and import keywords make this possible.

JavaScript provides different ways to export and import code. Named exports allow you to share multiple declarations from a module, default exports provide one primary export, import aliases let you use a different local name, and dynamic imports allow you to load modules when they are needed.

In this tutorial, you will learn how each feature works, when to use it, and how to apply it in real JavaScript projects.

What Are Named Exports in JavaScript?

Named exports allow you to export multiple functions, variables, or classes from a single JavaScript module. Each exported declaration has a specific name.

You can create named exports by placing the export keyword before a declaration or by exporting declarations together using curly braces.

Export Functions Using Named Exports

Create a file named math.js:

export function add(a, b) {
  return a + b;
}

export function subtract(a, b) {
  return a - b;
}

export function multiply(a, b) {
  return a * b;
}

Here, add(), subtract(), and multiply() are named exports.

You can import these functions into another JavaScript file and use them whenever needed.

Import Named Exports

Create a file named main.js:

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

console.log(add(10, 5));
console.log(subtract(10, 5));
console.log(multiply(10, 5));

Output:

15
5
50

The curly braces identify the named exports you want to import. The names must match the exported names unless you use import aliases.

The "./math.js" path tells JavaScript to load the module from the same directory as main.js.

Export Variables Using Named Exports

You can also export variables from a module.

Create config.js:

export const siteName = "Web4Learners";
export const siteLanguage = "English";
export const siteVersion = "1.0";

Import the variables into main.js:

import { siteName, siteLanguage, siteVersion } from "./config.js";

console.log(siteName);
console.log(siteLanguage);
console.log(siteVersion);

Output:

Web4Learners
English
1.0

Named exports are useful when a module contains several related values, such as application settings, utility functions, or configuration constants.

Export Declarations Together

You do not have to use the export keyword before every declaration. You can declare functions or variables first and export them together.

function greet() {
  return "Hello!";
}

function sayGoodbye() {
  return "Goodbye!";
}

export { greet, sayGoodbye };

This approach keeps the declarations together and makes it clear which parts of the module are available to other files.

What Are Default Exports in JavaScript?

A default export allows a module to provide one primary function, class, object, or other value.

A JavaScript module can have only one default export. However, it can also contain multiple named exports.

Create a Default Export

Create a file named greeting.js:

export default function greet() {
  return "Welcome to Web4Learners!";
}

The default keyword identifies the function as the module’s default export.

Import a Default Export

Create main.js:

import greet from "./greeting.js";

console.log(greet());

Output:

Welcome to Web4Learners!

Notice that the import statement does not use curly braces.

Unlike named imports, a default import can use a local name chosen by the importing module.

For example:

import welcomeMessage from "./greeting.js";

console.log(welcomeMessage());

Output:

Welcome to Web4Learners!

The function is still the default export from greeting.js. Only its local name has changed.

Default Export a Class

You can use a default export when a file primarily represents one class.

Create Product.js:

export default class Product {
  constructor(name, price) {
    this.name = name;
    this.price = price;
  }

  getDetails() {
    return `${this.name}: ₹${this.price}`;
  }
}

Import and use the class:

import Product from "./Product.js";

const product = new Product("Keyboard", 1200);

console.log(product.getDetails());

Output:

Keyboard: ₹1200

This pattern is common when a file is dedicated to one main class or component.

Named Exports and Default Exports Together

A single module can have one default export and multiple named exports.

For example, create user.js:

export const userName = "Karim";

export function getUserRole() {
  return "Developer";
}

export default function getUserMessage() {
  return `Welcome, ${userName}!`;
}

You can import the default and named exports together:

import getUserMessage, {
  userName,
  getUserRole
} from "./user.js";

console.log(getUserMessage());
console.log(userName);
console.log(getUserRole());

Output:

Welcome, Karim!
Karim
Developer

The default import appears without curly braces, while the named imports appear inside curly braces.

What Are Import Aliases in JavaScript?

Import aliases allow you to give an imported declaration a different local name using the as keyword.

Aliases are useful when two modules export declarations with the same name or when you want an imported name to be more descriptive.

Rename a Named Import

Suppose math.js exports an add() function:

export function add(a, b) {
  return a + b;
}

You can import it under another name:

import { add as calculateSum } from "./math.js";

console.log(calculateSum(10, 20));

Output:

30

Here, add is the exported name, and calculateSum is the local name used in main.js.

You must use calculateSum() in this file because that is the name assigned to the imported binding.

Rename Multiple Imports

You can rename several imports from the same module.

Suppose math.js contains:

export function add(a, b) {
  return a + b;
}

export function subtract(a, b) {
  return a - b;
}

Import both functions with aliases:

import {
  add as sum,
  subtract as difference
} from "./math.js";

console.log(sum(20, 10));
console.log(difference(20, 10));

Output:

30
10

This can improve readability when a project uses similar function names across different modules.

Rename an Export

You can also assign a different exported name when exporting a declaration.

function greet() {
  return "Hello, developer!";
}

export { greet as sayHello };

Another module can import it using the exported name:

import { sayHello } from "./greeting.js";

console.log(sayHello());

Output:

Hello, developer!

The original function is named greet, but the module exposes it as sayHello.

Rename a Namespace Import

You can import all named exports through a namespace and assign the namespace a local name.

import * as MathTools from "./math.js";

console.log(MathTools.add(5, 3));
console.log(MathTools.subtract(5, 3));

Output:

8
2

The name MathTools represents the module namespace. You access its named exports through properties such as MathTools.add.

This technique is useful when you want to group related functions under one name.

What Are Dynamic Imports in JavaScript?

Dynamic imports allow you to load a JavaScript module when your program needs it rather than importing it using a static import declaration at the beginning of the file.

JavaScript uses the import() function-like syntax for dynamic imports.

Unlike static imports, dynamic imports can be placed inside functions, conditional statements, event handlers, and other parts of your code.

Dynamic imports return a Promise that resolves to a module namespace object containing the module’s exports.

They are useful for loading features on demand, reducing the initial amount of JavaScript a page needs to load, and separating large applications into smaller parts.

Basic Dynamic Import Example

Create a file named message.js:

export function showMessage() {
  return "This module was loaded dynamically!";
}

Now create main.js:

import("./message.js")
  .then((module) => {
    console.log(module.showMessage());
  })
  .catch((error) => {
    console.error("Failed to load the module:", error);
  });

Output:

This module was loaded dynamically!

Here is how the example works:

  • import("./message.js") requests the module dynamically.
  • The returned Promise resolves when the module has loaded successfully.
  • The module parameter represents the module namespace object.
  • module.showMessage() calls the exported function.
  • The catch() block handles errors if loading fails.

Because dynamic imports are asynchronous, the program does not wait synchronously for the module to load.

Dynamic Imports With async and await

You can use async and await to make dynamic imports easier to read.

async function loadMessage() {
  try {
    const module = await import("./message.js");

    console.log(module.showMessage());
  } catch (error) {
    console.error("Failed to load the module:", error);
  }
}

loadMessage();

Output:

This module was loaded dynamically!

The await keyword pauses the execution of loadMessage() until the import Promise settles. The rest of the application can continue running while the module is being loaded.

For a refresher, see JavaScript Async and Await.

Dynamic Import With Conditional Logic

You can load a module only when a particular condition is met.

For example, create admin.js:

export function openAdminPanel() {
  console.log("Admin panel opened.");
}

In main.js:

const isAdmin = true;

async function openDashboard() {
  if (isAdmin) {
    const module = await import("./admin.js");

    module.openAdminPanel();
  } else {
    console.log("Access denied.");
  }
}

openDashboard();

Output when isAdmin is true:

Admin panel opened.

When the condition is false, the code inside the if block does not run, so the dynamic import is not requested by this code path.

In a real application, the server must still enforce authorization. A conditional import is a code-loading technique, not a security mechanism.

Dynamic Imports With Default Exports

Dynamic imports also work with modules that provide a default export.

Create calculator.js:

export default function calculateSquare(number) {
  return number * number;
}

Import and use the default export dynamically:

async function calculate() {
  const module = await import("./calculator.js");

  console.log(module.default(6));
}

calculate();

Output:

36

A dynamic import resolves to a module namespace object. The default export is available through its default property, which is why this example uses module.default().

You can also rename the imported default value locally:

async function calculate() {
  const { default: square } = await import("./calculator.js");

  console.log(square(6));
}

calculate();

Output:

36

This example uses object destructuring to assign the default export to a local variable named square.

Static Imports vs Dynamic Imports

Static and dynamic imports serve different purposes.

FeatureStatic ImportDynamic Import
Syntaximport { add } from "./math.js";import("./math.js")
Loading approachDeclared at module levelRequested when execution reaches the import
AsynchronousModule loading is handled by the module systemReturns a Promise
Conditional useCannot be placed inside an ordinary conditional blockCan be used inside conditional blocks and functions
Access to exportsImported bindingsModule namespace object
Common useCore application functionalityOptional or on-demand functionality

Static imports are usually best for dependencies that a module always needs. Dynamic imports are useful for features that should load only when required.

Real-World Example: Load a Feature on Button Click

Imagine a website with a button that displays additional information only when the visitor clicks it.

Project structure:

dynamic-app/
├── index.html
├── main.js
└── details.js

Create details.js

export function showDetails() {
  const message = document.createElement("p");

  message.textContent = "These details were loaded on demand.";

  document.body.appendChild(message);
}

Create main.js

const button = document.querySelector("#showDetails");

button.addEventListener("click", async () => {
  try {
    const module = await import("./details.js");

    module.showDetails();
  } catch (error) {
    console.error("Unable to load details:", error);
  }
});

Create index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Dynamic Import Example</title>
</head>
<body>
  <h1>Dynamic Imports</h1>

  <button id="showDetails">Show Details</button>

  <script type="module" src="./main.js"></script>
</body>
</html>

Run the example using a local development server. When the visitor clicks the button, the browser requests details.js if it has not already loaded it, then runs showDetails().

This approach can be useful for optional dashboards, charts, advanced editors, and other features that do not need to load immediately. Actual performance improvements depend on the application and how its build tools bundle the code.

Common Mistakes When Using Exports and Imports

Using Curly Braces Incorrectly

Named imports use curly braces:

import { add } from "./math.js";

Default imports do not:

import calculate from "./calculator.js";

Make sure the syntax matches the type of export.

Importing the Wrong Name

If a module exports calculateTotal, this import will fail unless a matching default or named export exists:

import { calculateSum } from "./math.js";

Use the actual exported name or an alias:

import { calculateTotal as calculateSum } from "./math.js";

Forgetting That Dynamic Imports Return Promises

Incorrect:

const module = import("./message.js");

module.showMessage();

Here, module is a Promise, not the loaded module namespace object.

Correct:

const module = await import("./message.js");

module.showMessage();

Use await inside an async function or handle the Promise with .then().

Using an Incorrect Module Path

A relative path such as "./math.js" points to a file relative to the importing module. Check the directory structure and filename if the browser or runtime cannot resolve the module.

Assuming Dynamic Imports Provide Security

Dynamic imports control when code is loaded; they do not prevent visitors from accessing or inspecting client-side code. Sensitive operations and permissions must be enforced on the server.

Frequently Asked Questions

What is the difference between named exports and default exports?

Named exports allow a module to expose multiple declarations by name. A default export identifies one primary value or declaration that can be imported under a chosen local name.

Can I use named exports and default exports in the same file?

Yes. A module can have multiple named exports and one default export.

What is an import alias in JavaScript?

An import alias gives an imported declaration a different local name. For example, import { add as sum } from "./math.js"; imports the named export add as sum.

What is a dynamic import in JavaScript?

A dynamic import uses import() to load a module asynchronously when needed. It returns a Promise that resolves to a module namespace object.

When should I use dynamic imports?

Use dynamic imports for optional features, large modules that are not immediately needed, or functionality that should load after a visitor performs an action.

Can dynamic imports load default exports?

Yes. The default export is available through the resolved module namespace object’s default property.

Do dynamic imports work in modern browsers?

Yes. Modern browsers support dynamic imports, but the imported module must be available at a valid URL and comply with the environment’s module-loading rules.

Conclusion

Named exports, default exports, import aliases, and dynamic imports are important features of modern JavaScript modules.

Named exports make it easy to share multiple declarations, default exports provide a module’s primary value, and aliases help avoid naming conflicts. Dynamic imports make it possible to load optional functionality only when needed.

Understanding these features will help you organize larger JavaScript projects, reuse code effectively, and build applications with more flexible loading behavior.