import(specifier) is an expression that returns a promise. It does the same three phases as a static import (load the graph, link, evaluate), but only when the expression runs, and it fulfills with the module's namespace object. Default exports are on .default.
How it differs from static import:
- Run time, not link time. The specifier is any expression converted with
ToString, so it can be computed or conditional. A staticimportis hoisted and runs before the module body. - Available everywhere. It works in classic scripts, CommonJS, and ES modules. In CommonJS it is the supported way to load an ES module that uses top-level
await. - Asynchronous. The promise settles after the fetch, link, and evaluation, even if the module is already cached.
- Cached. The same resolved module gives the same namespace object and is evaluated once. Two calls share one load.
- Errors reject. A missing file, a link error (
SyntaxErrorfor a missing export), an evaluation error, or an unsupported option all reject the promise. Evaluation errors are remembered, so a secondimport()of the same module rejects with the same error.
It is syntax, not a function: import.call(...) and const f = import are SyntaxErrors, and there is no import binding to override.
Relative specifiers resolve against the module that contains the import() call, not the working directory. The second argument is an options object ({ with: { type: 'json' } }) for import attributes.
The main use is lazy loading: defer a heavy module until a feature is used, and let bundlers turn each import() into a separately downloaded chunk (covered in the tooling topic).