Skip to content
Elements

useElementVisibility

Tracks whether a DOM element is visible within the viewport (or a specified scroll container). Returns a reactive Observable<boolean> that updates automatically via the IntersectionObserver API.

Element Visibility
hidden

Scroll down in the box — the target turns green when ≥50% is visible.

↓ scroll down
target element
import {
const useRef$: {
<T = any>(initialValue: null): Ref$<T | null>;
<T = any>(initialValue: T): Ref$<T>;
<T = any>(): Ref$<T | null>;
}
useRef$
} from "@usels/core";
import {
const useElementVisibility: (element: MaybeEventTarget, options?: DeepMaybeObservable<UseElementVisibilityOptions>) => Observable<boolean>
useElementVisibility
} from "@usels/web";
function
function Component(): JSX.Element
Component
() {
const
const el$: Ref$<HTMLDivElement | null>
el$
=
useRef$<HTMLDivElement>(): Ref$<HTMLDivElement | null> (+2 overloads)

Core (framework-agnostic) version of useRef$. Creates an observable element ref with opaque wrapping.

  • non-null value → Ref$<T>: current, get(), peek() return T
  • null / no arg → Ref$<T | null>: current, get(), peek() return T | null

Nullability is expressed via the type parameter, mirroring T | null at the call site.

useRef$
<
interface HTMLDivElement
HTMLDivElement
>();
const
const isVisible$: any
isVisible$
=
function useElementVisibility(element: MaybeEventTarget, options?: DeepMaybeObservable<UseElementVisibilityOptions>): Observable<boolean>

Framework-agnostic reactive element visibility tracker.

Tracks whether a DOM element is intersecting the viewport (or a specified scroll container). Returns a reactive Observable<boolean> that updates automatically via the IntersectionObserver API.

@paramelement - Element to observe (Ref$, Observable element, or raw element).

@paramoptions - Optional configuration — plain, per-field Observable, or fully Observable.

@returnsObservable<boolean> — true when element is intersecting.

useElementVisibility
(
const el$: Ref$<HTMLDivElement | null>
el$
);
return <
JSX.IntrinsicElements.div: DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>
div
RefAttributes<HTMLDivElement>.ref?: Ref<HTMLDivElement> | undefined

Allows getting a ref to the component instance. Once the component unmounts, React will set ref.current to null (or call the ref with null if you passed a callback ref).

ref
={
const el$: Ref$<HTMLDivElement | null>
el$
} />;
}
const isVisible$ = useElementVisibility(el$, { initialValue: true });

Use once: true to automatically stop observing after the element becomes visible for the first time:

const isVisible$ = useElementVisibility(el$, { once: true });

Pass a scrollTarget to observe intersection within a scrollable container instead of the viewport:

const container$ = useRef$<HTMLDivElement>();
const isVisible$ = useElementVisibility(el$, { scrollTarget: container$ });
const isVisible$ = useElementVisibility(el$, {
threshold: 0.5,
rootMargin: "0px 0px -100px 0px",
});

All options accept Observable<T> for reactive control:

import { observable } from "@usels/core";
const threshold$ = observable<number | number[]>(0.5);
const rootMargin$ = observable("0px");
const isVisible$ = useElementVisibility(el$, {
threshold: threshold$,
rootMargin: rootMargin$,
});
// later — update reactively
threshold$.set(0.75);
rootMargin$.set("-50px 0px");
ParameterTypeDescription
elementMaybeEventTarget- Element to observe (Ref$, Observable element, or raw element).
optionsUseElementVisibilityOptions (optional)- Optional configuration — plain, per-field Observable, or fully Observable.
OptionTypeDefaultDescription
initialValueboolean-Initial visibility value. Default: false
scrollTargetMaybeEventTarget-Element used as the viewport for intersection. Maps to IntersectionObserver root.
rootMarginstring-Margin around the root. Accepts CSS-style values. Default: “0px”
thresholdnumber | number[]-Threshold(s) at which to trigger. Default: 0
onceboolean-Stop observing after the element becomes visible for the first time. Default: false. Must be set at mount time; changing dynamically has no effect.

ObservableBoolean<boolean>

NameTypeDescription
toggle() => void-
peek{ (): boolean; (): boolean; }-
get(trackingType?: TrackingType | GetOptions) => boolean-
onChange(cb: ListenerFn<boolean>, options?: { trackingType?: TrackingType; initial?: boolean | undefined; immediate?: boolean | undefined; noArgs?: boolean | undefined; } | undefined) => () => void-
set{ (value: (prev: boolean) => boolean): void; (value: ObservableBoolean<boolean>): void; (value: boolean | ImmutableObservableBase<boolean> | ... 4 more ... | (() => boolean | ... 1 more ... | Promise<...>)): void; (value: Promise<...>): void; (value: boolean): void; }-
delete() => void-

View on GitHub