Dynamic Imports
A normal import loads everything up front. import() loads a module only when you need it — making your first page load faster.
On this page · 5 min · Advanced
On this page
- Learning objectives
- Explanation
- Syntax
- Code example
- Step by step
- Real-world use
- Common mistakes
- Best practices
- Remember this
- Practice exercise
- Mini quiz
- Related topics
- My notes
- Reading time
- 5 min
- Difficulty
- Advanced
- Updated
- Oct 28, 2026
Related tutorials
Learning objectives#
- Load a module on demand with import()
- Read default and named exports from the result
- Use dynamic imports for code splitting
- Handle loading errors and show loading states
Explanation#
import("./chart.js") is a function-like expression that returns a promise for the module object. It can be used anywhere — inside if statements, event handlers or functions.
The resolved module has named exports as properties and the default export under .default: const { default: Chart, formatAxis } = await import("./chart.js").
Bundlers like Vite and webpack see import() and split that module (and its dependencies) into a separate file that is downloaded only when called.
Typical uses: heavy libraries (charts, PDF, editors), rarely used pages, admin areas, language files and polyfills for old browsers.
Top-level await (ES2022) lets a module wait for a dynamic import directly at the top level, without wrapping it in an async function.
Syntax#
const module = await import('./heavy.js');
module.default(); // default export
module.namedThing(); // named export
Code example: Load a chart library only when clicked#
const button = document.querySelector('#show-chart');
const status = document.querySelector('#status');
button.addEventListener('click', async () => {
status.textContent = 'Loading chart…';
try {
// chart.js and its dependencies are downloaded only now
const { default: renderChart, formatRupees } = await import('./chart.js');
renderChart('#sales', [120, 340, 210], { label: formatRupees });
status.textContent = '';
} catch (err) {
status.textContent = 'Could not load the chart. Check your connection.';
console.error(err);
}
});
// Load a translation file based on the user's language
const lang = navigator.language.startsWith('hi') ? 'hi' : 'en';
const { messages } = await import(`./i18n/${lang}.js`); // top-level await
document.title = messages.title;
// React equivalent: const Reports = React.lazy(() => import('./Reports.jsx'));
The page loads without chart.js. Clicking the button shows “Loading chart…”, downloads chart.js, then draws the chart — or shows a friendly error if the download fails.
This example is illustrative. Copy or download it and run it in your own environment; the expected output is shown above.
Step-by-step explanation#
- The click handler is async so it can await the import.
- Destructuring pulls the default export (renamed renderChart) and a named export.
- The bundler puts chart.js in its own file that is fetched on first click; later clicks reuse the cached module.
- try/catch handles network failures with a helpful message.
- A template literal chooses the language file at runtime.
- React.lazy uses exactly the same import() under the hood.
Real-world use case#
An e-commerce site loads its 300 KB product-zoom library only when a shopper opens the zoom view, keeping the product page fast on mobile networks.
Common mistakes#
const Chart = await import("./chart.js") gives the module object, not the default export. Use (await import(…)).default or destructure { default: Chart }.
The download can take time or fail. Show “Loading…” and handle errors.
import(userInput) cannot be analysed by bundlers and is a security risk. Keep a fixed folder and limited set of names.
Best practices#
- Split heavy, rarely used features
- Keep the critical first-page code in static imports
- Preload likely-needed chunks on hover or idle
- Always handle loading and error states
Remember this#
import X from … = pack it now. import(…) = order it when needed.
import() returns a promise → await it.
Default export lives at .default.
Practice exercise#
Your task: Write a function openEditor() that dynamically imports "./editor.js", calls its default export with "#editor", and shows an alert if loading fails.
Need a hint?
Show solution
async function openEditor() {
try {
const { default: createEditor } = await import('./editor.js');
createEditor('#editor');
} catch {
alert('The editor could not be loaded. Please try again.');
}
}
Mini quiz#
What does import("./x.js") return?
Want more? Practise all ES6+ MCQs · ES6+ interview questions
My notes#
Saved in this browser.