Reading — step 1 of 7
Learn
You met import/export in JavaScript Fundamentals. Now we'll go deeper: dynamic imports, top-level await, side-effect imports, and the gotchas of mixing ESM with CommonJS.
Static vs dynamic imports
Static — at the top of the file, hoisted, dependencies known at parse time:
Dynamic — anywhere in code, runs at the call site, returns a Promise:
Why dynamic?
- Code splitting — bundlers split the imported module into a separate chunk
- Conditional loading —
if (user.isAdmin) await import('./adminPanel.js') - Lazy/deferred loading — improve startup time by deferring rare-use code
- Plugin systems — load modules whose paths come from config
Top-level await
In ESM (browsers, Node 14+), you can use await at the top of a module:
Importing modules wait for it:
Used for: loading WASM, fetching configuration, opening DB connections during module init. Use sparingly — slows down the entire dependency graph.
Side-effect imports
Import for side effects only, no bindings:
Useful for global setup, polyfills, registering plugins.
ESM vs CommonJS — the divide
CommonJS (Node's original module system):
ESM (modern):
Key differences:
- ESM is async; CJS is sync
- ESM imports are HOISTED; CJS require can be conditional
- ESM imports are LIVE BINDINGS (read-only views of the source); CJS exports are snapshots
- Node treats
.jsfiles as CJS by default — set"type": "module"in package.json to switch to ESM - Use
.cjsto force CommonJS,.mjsto force ESM regardless of package.json
Interoperability in Node:
- ESM can
importCJS modules:import pkg from 'cjs-only-pkg'(default-importsmodule.exports) - CJS can
require()ESM only viaawait import(...)(since CJS can't await at top level, it's awkward)
import.meta
A special object available in modules with metadata:
Replaces CommonJS's __filename and __dirname (which don't exist in ESM).
Tree shaking and bundlers
With static imports, bundlers (Rollup, esbuild, Webpack) can do tree shaking — removing unused exports from the final bundle:
Dynamic imports prevent tree shaking (the bundler doesn't know what you'll grab) but enable code splitting.
Common mistakes
- Forgetting the file extension in Node ESM —
import './util'fails; needimport './util.js'. - Missing
"type": "module"in package.json — Node treats.jsas CommonJS by default. - Using
__filename/__dirnamein ESM — undefined; useimport.meta.url. - Top-level await blocking module loading — slows down anything depending on the module.
- Side effects in the top-level of a module — runs once on first import, but order across siblings can surprise. Prefer explicit init functions.
- Mixing ESM and CommonJS in the same file — not possible. Pick one per file.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…