JavaScript ES6 modules: import, export, and module patterns

Alex Chang Feb 2026
1 tab
// math.js - Named exports
export const PI = 3.14159;
export const E = 2.71828;

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

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

// Alternative export syntax
const subtract = (a, b) => a - b;
const divide = (a, b) => a / b;
export { subtract, divide };

// ===== Importing in another file =====

// Import specific exports
import { add, PI } from './math.js';
console.log(add(5, 3)); // 8

// Import with alias
import { add as sum } from './math.js';
console.log(sum(5, 3)); // 8

// Import everything
import * as Math from './math.js';
console.log(MathUtils.add(5, 3));
console.log(MathUtils.PI);

// Default exports
// user.js
export default class User {
  constructor(name) {
    this.name = name;
  }
  greet() {
    return `Hello, ${this.name}!`;
  }
}

// Importing default
import User from './user.js';
const user = new User('Alice');

// Mixing default and named
// utils.js
export default function log(message) {
  console.log(message);
}
export const version = '1.0.0';

// Import both
import log, { version } from './utils.js';

// Re-exporting
export { add } from './math.js';
export { default as User } from './user.js';
export * from './constants.js';

// Dynamic imports
async function loadModule() {
  const module = await import('./heavy-module.js');
  module.default.init();
}

// Conditional loading
if (condition) {
  import('./module-a.js').then(m => m.init());
}

// Lazy loading
button.addEventListener('click', async () => {
  const { Chart } = await import('./chart.js');
  const chart = new Chart(data);
  chart.render();
});

// Module pattern (pre-ES6)
const MyModule = (function() {
  let privateVar = 'secret';

  return {
    publicMethod() {
      console.log('Public');
    }
  };
})();

// Singleton with modules
class Database {
  constructor() {
    if (Database.instance) {
      return Database.instance;
    }
    Database.instance = this;
  }
  connect() {
    this.connection = 'Connected';
  }
}
export default new Database();

// Factory pattern
export function createShape(type) {
  switch (type) {
    case 'circle':
      return new Circle();
    case 'rectangle':
      return new Rectangle();
    default:
      throw new Error('Unknown shape');
  }
}

// Module aggregation
// components/index.js
export { Button } from './Button.js';
export { Input } from './Input.js';
export { Modal } from './Modal.js';

// Import all from one place
import { Button, Input } from './components/index.js';

// HTML usage
/*
<script type="module">
  import { add } from './math.js';
  console.log(add(5, 3));
</script>
*/
1 file · javascript Explain with highlit

ES6 modules organize code into separate files with export and import statements. I use export default for single main export and export { name } for named exports. The import { name } from './module.js' syntax imports specific exports. Using import * as Module from './module.js' imports everything as namespace object. The export { name as alias } renames exports while import { name as alias } renames imports. Dynamic imports with import('./module.js') load modules conditionally and return promises. Modules have their own scope, preventing global namespace pollution. The type="module" attribute enables ES6 modules in browsers. Module bundlers like Webpack and Rollup optimize module loading. Understanding module systems improves code organization and reusability.