useLayoutEffect
SSR‑safe drop‑in for React’s useLayoutEffect that no‑ops on the server to avoid hydration warnings, while preserving synchronous layout effects on the client.
useLayoutEffect
useLayoutEffect in React runs synchronously after DOM mutations and before the browser paints, making it ideal for reading layout or measuring elements. However, calling it during server‑side rendering produces warnings because it can’t run on the server.
This hook is an SSR‑safe drop‑in:
- On the client: it calls React’s
useLayoutEffect. - On the server: it becomes a no‑op (suppresses the warning), matching common best practices for universal apps.
Use this variant wherever you would use React’s useLayoutEffect in a codebase that may render on the server (Next.js, Remix, etc.). It prevents the “useLayoutEffect does nothing on the server” warnings.
API
function useLayoutEffect(
effect: React.EffectCallback,
deps?: React.DependencyList
): void;Prop
Type
Usage
1) Measure a DOM node safely (no server warnings)
import * as React from 'react';
import { useLayoutEffect } from '@loke/ui/use-layout-effect';
export function MeasuredBox() {
const ref = React.useRef<HTMLDivElement | null>(null);
const [size, setSize] = React.useState<{ w: number; h: number } | null>(null);
useLayoutEffect(() => {
if (!ref.current) return;
const el = ref.current;
setSize({ w: el.offsetWidth, h: el.offsetHeight });
}, []);
return (
<div>
<div ref={ref} className="inline-block rounded border p-4">
Measure me
</div>
<pre className="mt-2 text-xs text-muted-foreground">
{JSON.stringify(size)}
</pre>
</div>
);
}This is safe to import in SSR contexts because the hook no‑ops on the server and runs as a layout effect only on the client.
2) Setting initial scroll position or styles before paint
import * as React from 'react';
import { useLayoutEffect } from '@loke/ui/use-layout-effect';
export function ScrollToTopOnMount() {
useLayoutEffect(() => {
window.scrollTo({ top: 0, left: 0, behavior: 'instant' as ScrollBehavior });
}, []);
return null;
}Note: The effect only runs on the client—perfect for view tweaks that must happen before paint.
Behavior and differences from React
- Client: behaves like React’s
useLayoutEffect—runs synchronously after DOM mutations and before paint. - Server: no‑op to avoid warnings. This matches React guidance for universal code: neither
useEffectnoruseLayoutEffectrun on the server; suppressing the warning prevents noisy logs.
If you actually need a server‑side computation, perform it outside of React effects (e.g., during data fetching or in server components). Effects run in the browser only.
Best practices
- Prefer useLayoutEffect only when you need synchronous DOM reads/writes before paint (measuring, positioning).
- For side effects that don’t require pre‑paint synchronization (logging, data hydration), prefer useEffect.
- Guard DOM access with existence checks (e.g., if (!ref.current) return;) to avoid null references on initial render.
Limitations
- On the server, the effect won’t run (by design). If your logic must run during SSR, move it out of an effect and into your rendering/data pipeline.
- Avoid using layout effects to derive state that the server must also compute; otherwise, you may see a mismatch until hydration.
Import
import { useLayoutEffect } from '@loke/ui/use-layout-effect';useIsDocumentHidden
Track whether the current document is hidden or visible using the Page Visibility API — useful for pausing timers or deprioritizing work when the tab is backgrounded.
useId
Hydration‑safe, deterministic IDs for accessibility and labeling — SSR‑friendly and stable across client/server.