JavaScript modules
The export declaration is used to export values from a JavaScript module. Exported values can then be imported into other programs with the import declaration or dynamic import. The value of an imported binding is subject to change in the module that exports it — when a module updates the value of a binding that it exports, the update will be visible in its imported value.
In order to use the export declaration in a source file, the file must be interpreted by the runtime as a module. In HTML, this is done by adding type="module" to the <script> tag, or by being imported by another module. Modules are automatically interpreted in strict mode.
Browser support
| Feature | Desktop | Mobile | ||||
|---|---|---|---|---|---|---|
| Chrome | Edge | Firefox | Safari | Chrome Android | Safari iOS | |
| 61 | 16 | 60 | 10.1 | 61 | 10.3 | |
| HTML attribute | ||||||
nomodule | 61 | 16 | 60 | 11 | 61 | 11 |
| DOM API | ||||||
ecmascript_modules Support for ECMAScript modules | 80 | 80 | 114 | 15 | 80 | 15 |
| Other | ||||||
html.elements.script.type.module `type="module"` | 61 | 79 | 60 | 10.1 | 61 | 10.3 |
| 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. | 63 | 79 | 67 | 11.1 | 63 | 11.3 |
| The import.meta meta-property exposes context-specific metadata to a JavaScript module. It contains information about the module, such as the module's URL. | 64 | 79 | 62 | 11.1 | 64 | 12 |
| Operator | ||||||
| import.meta.resolve() is a built-in function defined on the import.meta object of a JavaScript module that resolves a module specifier to a URL using the current module's URL as base. | 105 | 105 | 106 | 16.4 | 105 | 16.4 |
| Statement | ||||||
arbitrary module namespace identifier names Arbitrary module namespace identifier names | 88 | 88 | 87 | 14.1 | 88 | 14.5 |
default `default` keyword with `export` | 61 | 16 | 60 | 10.1 | 61 | 10.3 |
namespace `export * as namespace` | 72 | 79 | 80 | 14.1 | 72 | 14.5 |
| Other | ||||||
| The static import declaration is used to import read-only live binding which are exported by another module. The imported bindings are called live bindings because they are updated by the module that exported the binding, but cannot be re-assigned by the importing module. | 61 | 16 | 60 | 10.1 | 61 | 10.3 |
| Statement | ||||||
arbitrary module namespace identifier names Arbitrary module namespace identifier names | 88 | 88 | 87 | 14.1 | 88 | 14.5 |
| The import attributes feature instructs the runtime about how a module should be loaded, including the behavior of module resolution, fetching, parsing, and evaluation. It's supported in import declarations, export...from declarations, and dynamic import(). | 123 | 123 | 138 | 17.2 | 123 | 17.2 |
worklet support Available in worklets | | | 114 | | | |
| Other | ||||||
svg.elements.script.type.module Experimental `type='module'` | | | 117 | | | |
- Nested workers support was introduced in Safari 15.5.
- Script loading in nested workers was introduced in Safari 16.4.
- Nested workers support was introduced in Safari on iOS 15.5.
- Script loading in nested workers was introduced in Safari on iOS 16.4.
- Module scripts without the `async` attribute do not load when the page is served as XHTML (`application/xhtml+xml`). See bug 40518469.
- Module scripts without the `async` attribute do not load when the page is served as XHTML (`application/xhtml+xml`). See bug 40518469.
- This feature was removed in a later browser version (79)
- Module scripts do not load when the page is served as XHTML (`application/xhtml+xml`).
- Module scripts without the `async` attribute do not load when the page is served as XHTML (`application/xhtml+xml`). See bug 40518469.
- Module scripts do not load when the page is served as XHTML (`application/xhtml+xml`).
Syntax
// Export
export const PI = 3.14159;
export function add(a, b) { return a + b; }
export default class Calculator { ... }
// Import
import Calculator, { PI, add } from './math.js';
import * as math from './math.js'; Use cases
-
Separating concerns
Move formatting, state, networking, and UI helpers into clear modules with explicit boundaries.
-
Optimized bundles
Static imports help bundlers remove unused code and understand dependency graphs more effectively.
Cautions
- Circular dependencies can produce confusing runtime states, so keep module boundaries intentional.
- Module organization should serve clarity, not create many tiny files with weak separation.
Accessibility
- A well-structured module system makes focus management, labeling, and announcement logic easier to keep consistent across a UI.