useVisible
Hook to know if an element is visible and optionally the visible area ration of the element.
Demo
INFO
The component renders a scrollable div with inside a bordered div element to which is attached _cbRef refCallback returned from useVisible hook. Component displays the bordered div visibility and ratio that change when parent div is scrolled.
Loading demo…
Show source code
tsx
import { useVisible } from "../../../..";
export const UseVisible = () => {
const [cbRef, isVisible, ratio] = useVisible(
{ mode: "ref" },
{
threshold: [0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1],
withRatio: true
}
);
return (
<div>
<div style={{ marginTop: 16, color: ratio === 1 ? '#98c379' : ratio === 0 ? '#e46962' : '#d2c04c' }}>
<p>isVisible: {isVisible ? 'visible' : 'hidden'}</p>
<p>ratio: {ratio}</p>
</div>
<div style={{ margin: 'auto', width: 300, height: 300, overflow: 'scroll', border: '1px solid' }}>
<p>scrollable div</p>
<div style={{ height: 800 }}>
<div
ref={cbRef}
style={{
border: '1px solid',
margin: '320px auto 0px',
height: 100,
width: 100,
textAlign: 'center',
}}
>
bordered div
</div>
</div>
</div>
</div>
);
}Types
UseVisibleAttachOptions
@templateT - The element type being observed.
Controls how the target element is attached to the {@link IntersectionObserver} used internally by useVisible.
ts
export type UseVisibleAttachOptions<T extends Element> =
| {
/**
* The hook returns a `RefCallback` to attach directly to the JSX element.
* The observer is connected when the ref is set and disconnected when the
* element is unmounted.
*/
mode: "ref";
targetRef?: never;
}
| {
/**
* - **`"effect"`** — The observer is connected inside a `useEffect` using
* the provided `targetRef`, running asynchronously after the browser paints.
* - **`"layout-effect"`** — The observer is connected inside a
* `useLayoutEffect` using the provided `targetRef`, running synchronously
* before the browser paints.
*/
mode: "effect" | "layout-effect";
/**
* A React `RefObject` pointing to the element to observe. Required when
* `mode` is `"effect"` or `"layout-effect"`.
*/
targetRef: RefObject<T>;
};UseVisibleOptions
Options accepted by useVisible. Extends {@link IntersectionObserverInit} with an additional withRatio flag.
ts
export type UseVisibleOptions = IntersectionObserverInit & {
/**
* When `true`, the hook returns a third tuple entry containing the current
* intersection ratio as a `number` between `0` and `1`, where `0` means the
* element is fully outside the viewport and `1` means it is fully visible.
* When `false` or omitted, only the boolean visibility flag is returned.
*/
withRatio?: boolean;
};UseVisibleResult
@templateT - The element type being observed.
Return value of useVisible when withRatio is false, undefined, or opts is omitted entirely.
| Index | Type | Description |
|---|---|---|
[0] | RefCallback<T> \| null | A React ref callback to attach to the target element. Only populated when attachOptions.mode is "ref" — null in "effect" and "layout-effect" modes where the target is provided via targetRef instead. |
[1] | boolean | Reactive boolean that is true when at least part of the target element intersects the viewport (or the configured root), and false otherwise. |
UseVisibleWithRatioResult
@templateT - The element type being observed.
Return value of useVisible when withRatio is true.
| Index | Type | Description |
|---|---|---|
[0] | RefCallback<T> \| null | A React ref callback to attach to the target element. Only populated when attachOptions.mode is "ref" — null in "effect" and "layout-effect" modes where the target is provided via targetRef instead. |
[1] | boolean | Reactive boolean that is true when at least part of the target element intersects the viewport (or the configured root), and false otherwise. |
[2] | number | The current intersection ratio as a number between 0 and 1. 0 means the element is fully outside the viewport; 1 means it is fully visible. Updated on every intersection change event. |
