Lazy loading
Components loaded on first use, and a prefetcher that loads them ahead of that once the app is idle.
import { lazyDialog, lazyNamed, prefetcher } from '@basmilius/react-ui';import { Suspense, useState } from 'react';
import { Button, lazyNamed, prefetcher } from '@basmilius/react-ui';
// A module the app splits off, loaded on its first use.
const Chart = lazyNamed(() => import('../shared/lazy-chart.tsx'), 'LazyChart');
export default function LazyLoadingDemo() {
const [shown, setShown] = useState(false);
return (
<div className="flex flex-col items-center gap-3">
<div className="flex gap-2">
<Button variant="secondary" onClick={() => setShown(!shown)}>
{shown ? 'Hide the chart' : 'Show the chart'}
</Button>
<Button onClick={() => void prefetcher.prefetchEverything()}>Prefetch everything</Button>
</div>
{shown && (
<Suspense fallback={<p className="text-xs text-text-muted">Loading...</p>}>
<Chart bars={[4, 7, 3, 9, 6]} />
</Suspense>
)}
</div>
);
}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.
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.
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 an app that reloads on a failed chunk can tell a prefetch's failure from a person's.
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.
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.