useDeferredComponent
The useDeferredComponent hook dynamically imports a component and renders it only after the host component has mounted on the client. On the server — and on the first client render, matching SSR — it returns null, so there is no hydration mismatch and the imported chunk stays out of the initial/critical path (the bundler still code-splits the dynamic import(), which is only invoked after hydration).
Use it for fire-and-forget, no-UI client trackers (analytics, speed insights) or any strictly client-side / below-the-fold widget whose code should not ship in the initial bundle.
import { useDeferredComponent } from '@archibald/client';
const Component = useDeferredComponent(FACTORY);
Parameters
| Name | Type | Description |
|---|---|---|
| factory | () => Promise<{ default: ComponentType<P> }> | A dynamic import returning a module with a default-exported component. Invoked once, on mount (later identities are ignored). |
Return value
| Type | Description |
|---|---|
ComponentType<P> | null | The loaded component, or null until it is imported after mount. |
useDeferredComponent vs React.lazy
Prefer useDeferredComponent when the component must not run during SSR or hydration. React.lazy + Suspense resolves on the server and loads during hydration (nearer the critical path, and it needs a Suspense boundary). useDeferredComponent renders null through SSR and the first client render, then loads strictly after mount — ideal for code that has no UI and should never block first paint.
Example
import { useDeferredComponent } from '@archibald/client';
// AnalyticsImpl and its heavy dependencies are code-split into an async chunk that is
// fetched only after hydration — never part of the initial/critical payload.
function Analytics() {
const Impl = useDeferredComponent(() => import('./AnalyticsImpl'));
return Impl ? <Impl /> : null;
}
export default Analytics;