File size: 13,307 Bytes
780c9fe | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 | ---
title: import()
slug: Web/JavaScript/Reference/Operators/import
page-type: javascript-operator
browser-compat: javascript.operators.import
sidebar: jssidebar
---
The **`import()`** syntax, commonly called _dynamic import_, is a function-like expression that allows loading an ECMAScript module asynchronously and dynamically into a potentially non-module environment.
Unlike the [declaration-style counterpart](/en-US/docs/Web/JavaScript/Reference/Statements/import), dynamic imports are only evaluated when needed, and permit greater syntactic flexibility.
## Syntax
```js-nolint
import(moduleName)
import(moduleName, options)
```
The `import()` call is a syntax that closely resembles a function call, but `import` itself is a keyword, not a function. You cannot alias it like `const myImport = import`, which will throw a {{jsxref("SyntaxError")}}.
[Trailing commas](/en-US/docs/Web/JavaScript/Reference/Trailing_commas) are only allowed if the runtime also supports `options`. Check [browser compatibility](#browser_compatibility).
### Parameters
- `moduleName`
- : The module to import from. The evaluation of the specifier is host-specified, but always follows the same algorithm as static [import declarations](/en-US/docs/Web/JavaScript/Reference/Statements/import).
- `options`
- : An object containing import options. The following key is recognized:
- `with`
- : The [import attributes](/en-US/docs/Web/JavaScript/Reference/Statements/import/with).
### Return value
Returns a promise which:
- If the referenced module is loaded and evaluated successfully, fulfills to a [module namespace object](#module_namespace_object): an object containing all exports from `moduleName`.
- If the [coercion to string](/en-US/docs/Web/JavaScript/Reference/Global_Objects/String#string_coercion) of `moduleName` throws, rejects with the thrown error.
- If module fetching and loading fails for any reason, rejects with an implementation-defined error (Node uses a generic `Error`, while all browsers use `TypeError`). Common causes may include:
- In a file-system-based module system (Node.js, for example), if accessing the file system fails (permission denied, file not found, etc.).
- In a web-based module system (browsers, for example), if the network request fails (not connected to the Internet, CORS issue, etc.) or an HTTP error occurs (404, 500, etc.).
- If evaluation of the referenced module throws, rejects with the thrown error.
> [!NOTE]
> `import()` never synchronously throws an error.
## Description
The import declaration syntax (`import something from "somewhere"`) is static and will always result in the imported module being evaluated at load time. Dynamic imports allow one to circumvent the syntactic rigidity of import declarations and load a module conditionally or on demand. The following are some reasons why you might need to use dynamic import:
- When importing statically significantly slows the loading of your code or increases your program's memory usage, and there is a low likelihood that you will need the code you are importing, or you will not need it until a later time.
- When the module you are importing does not exist at load time.
- When the import specifier string needs to be constructed dynamically. (Static import only supports static specifiers.)
- When the module being imported has side effects, and you do not want those side effects unless some condition is true. (It is recommended not to have any side effects in a module, but you sometimes cannot control this in your module dependencies.)
- When you are in a non-module environment (for example, `eval` or a script file).
Use dynamic import only when necessary. The static form is preferable for loading initial dependencies, and can benefit more readily from static analysis tools and [tree shaking](/en-US/docs/Glossary/Tree_shaking).
If your file is not run as a module (if it's referenced in an HTML file, the script tag must have `type="module"`), you will not be able to use static import declarations. On the other hand, the asynchronous dynamic import syntax is always available, allowing you to import modules into non-module environments.
The `options` parameter allows different kinds of import options. For example, [import attributes](/en-US/docs/Web/JavaScript/Reference/Statements/import/with):
```js
import("./data.json", { with: { type: "json" } });
```
Dynamic module import is not permitted in all execution contexts.
For example, `import()` can be used in the main thread, a shared worker, or a dedicated worker, but will throw if called within a [service worker](/en-US/docs/Web/API/Service_Worker_API) or a [worklet](/en-US/docs/Web/API/Worklet).
### Module namespace object
A _module namespace object_ is an object that describes all exports from a module. It is a static object that is created when the module is evaluated. There are two ways to access the module namespace object of a module: through a [namespace import](/en-US/docs/Web/JavaScript/Reference/Statements/import#namespace_import) (`import * as name from moduleName`), or through the fulfillment value of a dynamic import.
The module namespace object is a [sealed](/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/isSealed) object with [`null` prototype](/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object#null-prototype_objects). This means all string keys of the object correspond to the exports of the module and there are never extra keys. All keys are [enumerable](/en-US/docs/Web/JavaScript/Guide/Enumerability_and_ownership_of_properties) in lexicographic order (i.e., the default behavior of [`Array.prototype.sort()`](/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#description)), with the default export available as a key called `default`. In addition, the module namespace object has a [`[Symbol.toStringTag]`](/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/toStringTag) property with the value `"Module"`, used in {{jsxref("Object.prototype.toString()")}}.
The string properties are non-configurable and writable when you use {{jsxref("Object.getOwnPropertyDescriptors()")}} to get their descriptors. However, they are effectively read-only, because you cannot re-assign a property to a new value. This behavior mirrors the fact that static imports create "[live bindings](/en-US/docs/Web/JavaScript/Reference/Statements/import#imported_values_can_only_be_modified_by_the_exporter)" — the values can be re-assigned by the module exporting them, but not by the module importing them. The writability of the properties reflects the possibility of the values changing, because non-configurable and non-writable properties must be constant. For example, you can re-assign the exported value of a variable, and the new value can be observed in the module namespace object.
Each (normalized) module specifier corresponds to a unique module namespace object, so the following is generally true:
```js
import * as mod from "/my-module.js";
import("/my-module.js").then((mod2) => {
console.log(mod === mod2); // true
});
```
Except in one curious case: because a promise never fulfills to a [thenable](/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise#thenables), if the `my-module.js` module exports a function called `then()`, that function will automatically get called when the dynamic import's promise is fulfilled, as part of the [promise resolution](/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/Promise#the_resolve_function) process.
```js
// my-module.js
export function then(resolve) {
console.log("then() called");
resolve(1);
}
```
```js
// main.js
import * as mod from "/my-module.js";
import("/my-module.js").then((mod2) => {
// Logs "then() called"
console.log(mod === mod2); // false
});
```
> [!WARNING]
> Do not export a function called `then()` from a module. This will cause the module to behave differently when imported dynamically than when imported statically.
This aggressive caching ensures that a piece of JavaScript code is never executed more than once, even if it is imported multiple times. Future imports don't even result in HTTP requests or disk access. If you do need to re-import and re-evaluate a module without restarting the entire JavaScript environment, one possible trick is to use a unique query parameter in the module specifier. This works in non-browser runtimes that support URL specifiers too.
```js
import(`/my-module.js?t=${Date.now()}`);
```
Note that this can lead to memory leaks in a long-running application, because the engine cannot safely garbage-collect any module namespace objects. Currently, there is no way to manually clear the cache of module namespace objects.
You can also use the [Fetch API](/en-US/docs/Web/API/Fetch_API) to fetch module source code as text, and then evaluate the module manually depending on the module type:
- For JavaScript modules, you can dynamically import the source code as a [`blob:` URL](/en-US/docs/Web/API/URL/createObjectURL_static) in browsers, or use [`vm.Module`](https://nodejs.org/docs/latest/api/vm.html#class-vmmodule) to evaluate it in Node.js.
- For JSON modules, you can parse the source code using {{jsxref("JSON.parse()")}}.
- For CSS modules, you can create a new {{domxref("CSSStyleSheet")}} object and use its [`replace()`](/en-US/docs/Web/API/CSSStyleSheet/replace) method to populate it with the source code.
However, this is semantically not the same as dynamic import, because user-agent settings like [fetch destination](/en-US/docs/Web/API/Request/destination), [CSP](/en-US/docs/Web/HTTP/Guides/CSP), or [module resolution](/en-US/docs/Web/JavaScript/Reference/Operators/import.meta/resolve) may not be applied correctly.
Module namespace object caching only applies to modules that are loaded and linked _successfully_. A module is imported in three steps: loading (fetching the module), linking (mostly, parsing the module), and evaluating (executing the parsed code). Only evaluation failures are cached; if a module fails to load or link, the next import may try to load and link the module again. The browser may or may not cache the result of the fetch operation, but it should follow typical HTTP semantics, so handling such network failures should not be different from handling {{domxref("Window/fetch", "fetch()")}} failures.
## Examples
### Import a module for its side effects only
```js
(async () => {
if (somethingIsTrue) {
// import module for side effects
await import("/modules/my-module.js");
}
})();
```
If your project uses packages that export ESM, you can also import them for side
effects only. This will run the code in the package entry point file (and any files it
imports) only.
### Importing defaults
If you are destructuring the imported module namespace object, then you must rename the `default` key because `default` is a reserved word.
```js
(async () => {
if (somethingIsTrue) {
const {
default: myDefault,
foo,
bar,
} = await import("/modules/my-module.js");
}
})();
```
### Importing on-demand in response to user action
This example shows how to load functionality on to a page based on a user action, in this case a button click, and then call a function within that module. This is not the only way to implement this functionality. The `import()` function also supports `await`.
```js
const main = document.querySelector("main");
for (const link of document.querySelectorAll("nav > a")) {
link.addEventListener("click", (e) => {
e.preventDefault();
import("/modules/my-module.js")
.then((module) => {
module.loadPageInto(main);
})
.catch((err) => {
main.textContent = err.message;
});
});
}
```
### Importing different modules based on environment
In processes such as server-side rendering, you may need to load different logic on server or in browser because they interact with different globals or modules (for example, browser code has access to web APIs like `document` and `navigator`, while server code has access to the server file system). You can do so through a conditional dynamic import.
```js
let myModule;
if (typeof window === "undefined") {
myModule = await import("module-used-on-server");
} else {
myModule = await import("module-used-in-browser");
}
```
### Importing modules with a non-literal specifier
Dynamic imports allow any expression as the module specifier, not necessarily string literals.
Here, we load 10 modules, `/modules/module-0.js`, `/modules/module-1.js`, etc., concurrently, and call the `load` functions that each one exports.
```js
Promise.all(
Array.from({ length: 10 }).map(
(_, index) => import(`/modules/module-${index}.js`),
),
).then((modules) => modules.forEach((module) => module.load()));
```
### Using import attributes with dynamic import
[Import attributes](/en-US/docs/Web/JavaScript/Reference/Statements/import/with) are accepted as the second parameter of the `import()` syntax.
```js
const data = await import("./data.json", {
with: { type: "json" },
});
```
## Specifications
{{Specifications}}
## Browser compatibility
{{Compat}}
## See also
- [`import`](/en-US/docs/Web/JavaScript/Reference/Statements/import)
|