/// Utilities
ClientOnly
Defers its children until after the first client mount, so browser-only UI never runs during SSR.
import { ClientOnly } from "@protocore/pds";Deferring to the client
Wrap anything that depends on the browser — window, a non-deterministic value, a third-party embed — in ClientOnly. It renders fallback on the server and during the first paint, then swaps to children after mount, so hydration never mismatches.
When to use it
Server-rendered React must produce the same markup on the server and on the first client render, or hydration warns and can visibly flicker. ClientOnly is the escape hatch for the genuinely client-only cases: a widget that reads window.innerWidth, a value derived from Date.now(), an iframe embed that a crawler shouldn't see.
Reach for it sparingly — most SSR mismatches are better fixed at the source (a stable key, a fixed locale, suppressHydrationWarning on a single leaf). Provide a fallback that reserves the right amount of space so the swap doesn't shift layout.
Usage
Do
- Wrap only the subtree that truly needs the browser.
- Give a `fallback` that reserves the final layout size.
- Prefer it over sprinkling `typeof window` checks through render.
Don't
- Wrap the whole page; you lose SSR and SEO for everything.
- Use it to paper over a fixable hydration bug — fix the source.
- Expect its children in the server HTML; they render post-mount.
Accessibility
- Renders no wrapper element, so it adds nothing to the accessibility tree.
- Because content appears after mount, ensure the `fallback` communicates loading state where the wait is perceptible.
ClientOnly props
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | — | Rendered only after the component has mounted on the client. |
fallback | ReactNode | null | Rendered on the server and during the first client paint. |