---
url: https://adecore.dev/ui/utilities/lazy-loading.md
---
# Lazy loading

Components loaded on first use, and a prefetcher that loads them ahead of that once the app is idle.

```tsx
import { lazyDialog, lazyNamed, onLazyOpenError, prefetcher } from '@adecore/ui';
```

## lazyNamed

`lazyNamed(load, name)` is `React.lazy` for a module that exports its component by name, `default` included. It answers a component with the same props, which suspends until the module is there. Once the module is loaded it draws straight away. `React.lazy` suspends for a tick even on a module the bundler already has, which flashes the fallback and would mount a dialog already open.

```tsx
const HistoryPanel = lazyNamed(() => import('./HistoryPanel'), 'HistoryPanel');
```

## lazyDialog

`lazyDialog(load, name, useStore, isOpen)` is for a dialog that keeps its own open state in a store. Before its module is here, opening it loads it. Once it is here, prefetched or opened, it stays mounted while closed, so even its first opening animates like every later one.

```tsx
const SettingsWindow = lazyDialog(() => import('./SettingsWindow'), 'SettingsWindow', useAppStore, (state) => state.settingsOpen);
```

## The prefetcher

Every module loaded through `lazyNamed` or `lazyDialog` registers with `prefetcher`. Call `prefetcher.prefetchEverything()` once the app has drawn its first screen, and it loads every registered module ahead of its first use, one per idle moment and never two at once. A module registered afterwards, such as a lazy surface inside another, joins the end of the queue. `prefetcher.prefetch(load)` loads one module ahead of the rest.

The prefetcher does nothing while the browser asks to save data. A failed prefetch is quiet; the first real use tries again. `prefetcher.busy` is true while a prefetch waits on its chunk, so a failure an app hears of elsewhere, such as the bundler's event for a chunk that did not load, may be the prefetch's while it is true. It may as well be a person's open at the same moment, which `onLazyOpenError` tells apart.

`Prefetcher` is the class behind it, for a test or a second queue: `new Prefetcher(whenIdle, allowed?)`. `WhenIdle` is a function that calls back once the main thread is free, and `Loader` is a function that loads a module.

## A failed open

`onLazyOpenError(listener)` calls `listener` with the error when a module that `lazyNamed` or `lazyDialog` loads for a render fails to load, which is a load a person waits on. A failed prefetch never reaches it, not even one of the same module that fails at the same moment. It returns a function that stops listening.

`React.lazy` keeps a rejected load, so the surface that failed stays down until the page loads again. That makes this the place to reload once after a deploy removed the chunks of the build the page runs.

```tsx
const stop = onLazyOpenError((error) => console.warn('A lazy surface failed to load', error));
```

## LoadedComponent

`LoadedComponent` is the cache both helpers share: a loader, the component once it is loaded (`current`), `load()` and `subscribe(listener)`. You rarely need it directly.
