Technology · JavaScript

JavaScript Modules

Organize JavaScript code with ES6 modules using named exports, default exports, and dynamic imports.

TL;DR
  1. 01Export functions and variables with named or default export syntax.
  2. 02Import specific names with curly braces, defaults without them.
  3. 03Use dynamic import() to load modules on demand at runtime.

Named Exports

  • Export multiple items from a single module file.
    // math.js
    export function add(a, b) {
      return a + b;
    }
    
    export function subtract(a, b) {
      return a - b;
    }
    
    export const PI = 3.14159;
  • Import named exports using curly brace syntax.
    import { add, subtract, PI } from './math.js';
    
    console.log(add(5, 3)); // 8
  • Import all named exports under a namespace alias.
    import * as Math from './math.js';
    
    console.log(Math.add(5, 3));  // 8
    console.log(Math.PI);         // 3.14159
  • Export declarations inline or at the bottom of the file.
    // Inline
    export const name = 'Alice';
    
    // Bottom-of-file list
    const age = 30;
    export { age };
  • Re-export items from another module without importing them locally.
    // index.js — barrel file
    export { add, subtract } from './math.js';
    export { formatDate } from './date.js';

Default Export

  • Export one main item as the default export per file.
    // logger.js
    export default function log(message) {
      console.log(`[LOG] ${message}`);
    }
  • Import the default export without curly braces.
    import log from './logger.js';
    
    log('App started'); // [LOG] App started
  • Name the default import anything you want locally.
    import myLogger from './logger.js';
    import printMessage from './logger.js';
    // Both refer to the same default export
  • Export a class as the default export for component-style modules.
    // UserService.js
    export default class UserService {
      getUser(id) { /* ... */ }
    }
  • A module can have only one default export.
    // Valid: one default
    export default function main() { }
    
    // Invalid: two defaults
    // export default function a() { }
    // export default function b() { }

Mixing Named and Default

  • Combine named and default exports in the same module.
    // utils.js
    export default function main() {
      return 'main result';
    }
    
    export function helper() {
      return 'helper result';
    }
    
    export const VERSION = '1.0';
  • Import both the default and named exports in one statement.
    import main, { helper, VERSION } from './utils.js';
    
    main();
    helper();
    console.log(VERSION);
  • Re-export a default as a named export for barrel files.
    export { default as UserService } from './UserService.js';
  • Rename named exports during re-export for clarity.
    export { add as sum } from './math.js';
  • Use barrel files (index.js) to simplify deep import paths.
    // Without barrel: import UserService from '../../services/UserService.js';
    // With barrel:    import { UserService } from '../../services/index.js';

Renaming Imports

  • Rename imports with the as keyword to avoid conflicts.
    import { add as addition } from './math.js';
    
    addition(5, 3); // 8
  • Rename the default import to any local name you prefer.
    import logMessage from './logger.js';
    // Default import already has no fixed name
  • Rename when two modules export the same identifier.
    import { format as formatDate } from './date.js';
    import { format as formatCurrency } from './currency.js';
  • Rename exports when re-exporting from a barrel file.
    export { createUser as default } from './user.js';
  • Use renaming to expose a public API name different from internals.
    // Internal: camelCase
    export { _internalHelper as publicHelper } from './helpers.js';

Module Side Effects

  • Import a module purely for its side effects with no bindings.
    import './polyfills.js';     // Runs polyfill setup code
    import './analytics.js';    // Registers analytics listeners
  • Write side-effect code at the module top level to run on import.
    // init.js
    console.log('Module loaded');
    document.addEventListener('DOMContentLoaded', initApp);
  • Use side-effect imports for CSS in bundler environments.
    import './styles/global.css';
    import './styles/theme.css';
  • Modules are evaluated only once even if imported multiple times.
    import './init.js'; // runs once
    import './init.js'; // cached — does not run again
  • Use dynamic import() to load modules conditionally at runtime.
    async function loadChart() {
      const { Chart } = await import('./chart.js');
      return new Chart(document.getElementById('canvas'));
    }

Tip: Use named exports for multiple values and reserve default exports for the main thing a module provides — keeps imports predictable.

Warning: Circular imports can cause issues — avoid importing A from B while B imports from A, as one module may receive undefined values.

JavaScript EventsJavaScript Regular Expressions