Hardware FixRecommendedDevice not working? Your driver may be the problemCheck updates for common hardware issues.Fix DriversOctober DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsPC HealthRecommendedCrashes, freezes, slowdowns? Check your PC nowSpot repairable issues before they interrupt work.Check PC×
Skip to content
World desk5 min

JavaScript Modules Explained: ES Modules, Imports, Exports, and Best Practices

A practical guide to JavaScript ES modules: how imports and exports work, when Node.js requires extensions, how CommonJS interop differs, and how to align TypeScript settings with the runtime.
Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

JavaScript modules let you split a program into files with explicit dependencies: a module exports values, and another imports the values it needs. ES modules (ESM) are JavaScript’s standardized module format, but the rules for finding a module file depend on where the code runs. In particular, browser and bundler imports do not necessarily follow Node.js’s file-resolution rules.

What is a JavaScript module?

A module is a unit of JavaScript code that can share selected values with other modules. Its exports form an interface; code that is not exported remains private to that module. This lets a project divide responsibilities across files without putting every function and variable into one shared global scope.

ES modules use the export and import keywords. The language defines their syntax and semantics, but the host environment—such as a browser or Node.js—determines how an import specifier is resolved to a file or package. The TypeScript Handbook’s module theory explains this host-defined part of module behavior.

How do named imports and exports work?

A named export makes a particular binding available by name. The importing module requests that same name inside braces.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
// math.js
export function add(a, b) {
  return a + b;
}

// app.js
import { add } from './math.js';
console.log(add(2, 3)); // 5

Here, add is a named export. The relative specifier ./math.js identifies the module to load; whether its extension is required depends on the host. The example’s extension is valid for Node.js ESM and is also a clear choice in environments that accept it.

A module can expose multiple named exports, and an importer can request more than one:

// numbers.js
export const answer = 42;
export function double(value) {
  return value * 2;
}

// app.js
import { answer, double } from './numbers.js';

Names must match the exported bindings unless the importer uses an alias, such as import { double as twice } from './numbers.js';.

How is a default export different?

A default export is a separate export form, not an inherently better version of a named export. A module can have one default export, and the importing code chooses its local name without braces.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
// formatter.js
export default function format(value) {
  return String(value).trim();
}

// app.js
import clean from './formatter.js';

Use named exports when consumers should refer to specific exported names; use a default export when the module is naturally represented by one primary value. An import can combine a default import and named imports when a module provides both.

When should you use static import or dynamic import()?

A static import declaration belongs at a module’s top level. It makes the dependency explicit in the module source:

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

Use dynamic import() when loading should happen asynchronously or conditionally, for example after a user chooses a feature:

if (showAdvancedTools) {
  const tools = await import('./advanced-tools.js');
  tools.open();
}

Dynamic import returns a promise. It is useful for conditional loading, but it should not be presented as an automatic performance improvement: the effect depends on the runtime and build setup.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

How do you use ES modules in Node.js?

Node.js supports both ESM and CommonJS. Make the intended format explicit where possible; the Node.js ECMAScript modules documentation describes these markers and current syntax detection behavior.

Format Common markers
ES modules .mjs, or .js files in a package whose package.json sets "type": "module"
CommonJS .cjs, or .js files in a package whose package.json sets "type": "commonjs"

Node.js also supports input-type flags for code supplied through standard input or evaluated directly: --input-type=module selects ESM, while --input-type=commonjs selects CommonJS. If no explicit marker is present, current Node.js documentation also describes syntax detection; explicit markers make a project’s intent easier to see.

Include extensions in Node.js relative imports

For Node.js ESM, relative and absolute specifiers must include the file extension. Directory index files must also be specified fully. For example, use ./utils.js, not ./utils or ./utils/. These are Node.js resolution rules, not universal rules for browsers or bundlers.

// Node.js ESM
import { parse } from './utils/parse.js';

If an import works through a bundler but fails when running the emitted file directly in Node.js, check whether the specifier follows Node’s extension and package-resolution rules.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Understand package specifiers and exports

Node.js accepts relative specifiers such as ./startup.js, bare package specifiers such as some-package, and absolute URL specifiers. A package’s exports field can restrict which package subpaths consumers are allowed to import. A file existing inside a package does not by itself mean that consumers may import it through any path.

How does Node.js handle CommonJS interop?

Node.js ESM can import CommonJS modules. The reliable form is a default import: that default corresponds to the CommonJS module’s module.exports value.

// ESM file importing a CommonJS package
import legacyPackage from 'legacy-package';

Node.js may also make some CommonJS exports available as named imports through static analysis. This is best-effort rather than a guarantee: some export patterns may not be detected, and inferred named exports do not reflect later changes to the CommonJS exports object. Use the default import when dependable access to module.exports is needed.

Node.js require() supports only synchronous ES modules. An ES module that uses top-level await cannot be loaded that way. Interoperability behavior can differ among Node.js, browsers, bundlers, transpilers, and TypeScript, so avoid assuming one environment’s rules apply everywhere.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

How should TypeScript module settings match the runtime?

TypeScript’s module configuration and module-resolution configuration describe how the compiler should model the environment that will execute the code. They do not make every runtime resolve files the same way. Choose settings for the actual destination rather than treating one configuration as universal.

Code intended to run in Node.js

The current TypeScript Modules Reference recommends the node16, node18, or nodenext module modes for Node.js projects. These modes model Node’s dual ESM/CommonJS system and select behavior based on each file’s detected format. They do not mean “ESM only”; TypeScript can emit ESM or CommonJS according to that format.

Code processed by a bundler

For code a bundler processes, TypeScript documents bundler-oriented module resolution. Set module and moduleResolution to reflect the actual pipeline—especially whether the bundler processes TypeScript source directly or whether emitted JavaScript is later run by Node.js. A configuration that models a bundler may not accurately model direct Node.js execution.

Practical checks when an import fails

  • Identify the host. Determine whether the code runs in a browser, Node.js, or through a bundler or transpiler.
  • Check the file’s module format. In Node.js, look for .mjs or the nearest package’s "type" setting; CommonJS can use .cjs or "type": "commonjs".
  • Check the specifier. For Node.js ESM relative imports, include the extension and any directory index filename.
  • Check the package’s public paths. A package’s exports field may block a subpath that appears to exist in its files.
  • Check import form and exported names. Named imports must refer to named exports; for a CommonJS module in Node.js, the default import is the dependable form.
  • Align TypeScript with execution. Confirm that its module and resolution settings represent the runtime or bundler that actually handles the code.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

More from the Wire

  1. Shenzhen desk3 min
    HONOR Expands Beyond Smartphones With Humanoid Robot RevealHONOR said it unveiled its first humanoid robot at MWC 2026 and named shopping assistance, workplace inspections, and supportive companionship as intended uses. Later Robotics D1 claims and a reported…
  2. Cupertino desk5 min
    Apple Unveils AirPods Max 2: The Upgrade That Should Have Happened Years AgoAirPods Max 2 adds H2-powered audio features and Apple claims up to 1.5× more effective ANC, but its design, Smart Case, and 20-hour battery rating are unchanged. Wired lossless audio…
  3. Cupertino desk4 min
    Apple’s OLED Touch MacBooks Are Coming—but the Dynamic Island Is the Real GambleApple has not announced an OLED touchscreen MacBook, but reports point to high-end models arriving in late 2026 or early 2027. The reported Mac Dynamic Island could be useful, but…
Recommended PC Tool
Recommended PC Tool
Outdated Drivers Are Slowing You DownFree scan - exact matches
Windows Errors? Fix Them Before They SpreadFree repair scan

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.