JavaScript Module Import

The import statement is used to bring in functionality exported from other modules. JavaScript provides several import syntaxes for different use cases, including named imports, default imports, and dynamic imports.

Import Named Exports

Named exports are imported using curly braces {}. The import names must match the export names exactly (unless renamed with 'as').

Importing Named Exports
// From a module: export const PI = 3.14;
//                 export function add(a, b) { return a + b; }

// import { PI, add } from './math.js';

// Simulating the import
const { PI, add } = { PI: 3.14159, add: (a, b) => a + b };

console.log('PI = ' + PI);
console.log('add(10, 20) = ' + add(10, 20));

Import Default Export

Default exports are imported without curly braces. You can give them any name you like since there is only one default export per module.

Importing Default Exports
// From a module: export default class User { ... }

// import User from './user.js';
// import MyUser from './user.js';  // Any name works!

// Simulating default import
class User {
  constructor(name) {
    this.name = name;
  }
  greet() {
    return 'Hello, ' + this.name;
  }
}

// You can use any name for default imports
const MyUser = User;
const u = new MyUser('Alice');
console.log(u.greet());

Import as (Renaming)

You can rename imports using the 'as' keyword. This is useful to avoid naming conflicts or to use shorter names.

Renaming Imports with as
// import { calculateTotal as total } from './calc.js';
// import { calculateAverage as avg } from './calc.js';

// Simulating renamed imports
const calculateTotal = (items) => items.reduce((s, i) => s + i, 0);
const calculateAverage = (items) => calculateTotal(items) / items.length;

// Renaming with destructuring (simulates 'as')
const total = calculateTotal;
const avg = calculateAverage;

const data = [10, 20, 30, 40];
console.log('Total: ' + total(data));
console.log('Average: ' + avg(data));

Import All (*)

You can import everything from a module as a namespace object using * as name. All named exports become properties of the namespace object.

Import All with * as
// import * as MathUtils from './math.js';
// MathUtils.add(1, 2);
// MathUtils.PI;

// Simulating namespace import
const MathUtils = {
  PI: 3.14159,
  add: (a, b) => a + b,
  subtract: (a, b) => a - b,
  multiply: (a, b) => a * b
};

console.log('PI: ' + MathUtils.PI);
console.log('add: ' + MathUtils.add(5, 3));
console.log('subtract: ' + MathUtils.subtract(10, 4));
console.log('multiply: ' + MathUtils.multiply(6, 7));

// List all exports
console.log('Exports: ' + Object.keys(MathUtils));

Side-Effect Imports

Sometimes you want to import a module only for its side effects (e.g., polyfills, CSS, or initialization code) without importing any specific values. Use the import statement without any bindings.

Side-Effect Imports
// import './polyfill.js';  // Runs the module but imports nothing
// import './styles.css';   // With bundler support

// The module runs its code when imported:
// polyfill.js:
// if (!Array.prototype.flat) {
//   Array.prototype.flat = function() { ... };
// }

// Simulating a side effect
console.log('Side-effect import: module code runs on import');
console.log('No values are imported');
console.log('Used for: polyfills, CSS, analytics, initialization');

Dynamic import()

Dynamic import() allows you to load modules on demand at runtime. It returns a promise that resolves to the module. This is useful for code splitting and lazy loading.

Dynamic import()
// Dynamic import returns a promise
// const module = await import('./heavy-module.js');

// Conditional loading
async function loadModule(name) {
  // In real code: const mod = await import(`./modules/${name}.js`);
  const modules = {
    math: { add: (a, b) => a + b },
    string: { upper: (s) => s.toUpperCase() }
  };

  const mod = modules[name];
  if (!mod) throw new Error('Module not found: ' + name);
  return mod;
}

async function main() {
  const math = await loadModule('math');
  console.log('Dynamic add: ' + math.add(5, 3));

  const str = await loadModule('string');
  console.log('Dynamic upper: ' + str.upper('hello'));
}

main();
Import TypeSyntaxUse Case
Namedimport { x } from '...'Import specific exports
Defaultimport X from '...'Import main export
Renamedimport { x as y } from '...'Avoid naming conflicts
Namespaceimport * as M from '...'Import everything
Side-effectimport '...'Run module code only
Dynamicimport('...')Lazy loading / code splitting
Combinedimport X, { y } from '...'Default + named together
📝 Note: Static imports (import ... from) must be at the top level of a module and cannot be inside conditionals or functions. For conditional loading, use dynamic import(). Dynamic import() works in both modules and regular scripts.
Exercise:
How do you import all named exports from a module as a single object?
Try it YourselfCtrl+Enter to run
Click Run to see the output here.