ES6+ (Modern JavaScript) · ES2020 and Later

Dynamic Imports

A normal import loads everything up front. import() loads a module only when you need it — making your first page load faster.

Advanced 5 min Lesson 19 of 23
Before you start:ModulesAsync and Await
0/23 in ES6+
On this page · 5 min · Advanced

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#

JavaScript Illustrative
const module = await import('./heavy.js');
module.default();      // default export
module.namedThing();   // named export

Code example: Load a chart library only when clicked#

JavaScriptapp.js Illustrative
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'));
Expected output
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#

  1. The click handler is async so it can await the import.
  2. Destructuring pulls the default export (renamed renderChart) and a named export.
  3. The bundler puts chart.js in its own file that is fetched on first click; later clicks reuse the cached module.
  4. try/catch handles network failures with a helpful message.
  5. A template literal chooses the language file at runtime.
  6. 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#

Forgetting .default

const Chart = await import("./chart.js") gives the module object, not the default export. Use (await import(…)).default or destructure { default: Chart }.

No loading or error state

The download can take time or fail. Show “Loading…” and handle errors.

Fully dynamic paths

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?
async function, try/catch, destructure default.
Show solution
JavaScript Illustrative
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#

Dynamic Imports quizMultiple choice Question 1 of 3

What does import("./x.js") return?

Score so far: 0/0

Want more? Practise all ES6+ MCQs · ES6+ interview questions

My notes#

Saved in this browser.

Progress is saved in this browser. Sign in to sync.