Skip to content

useElementBounding

Tracks the bounding rect of a DOM element — x, y, top, right, bottom, left, width, height — as reactive Observable<number> values. Uses ResizeObserver for size changes, MutationObserver for style/class attribute changes, and scroll/resize window events for position changes. requestAnimationFrame is used by default so CSS transform animations are captured accurately.

x: 0pxy: 0pxtop: 0pxleft: 0pxwidth: 0pxheight: 0px
resize me ↘
import {
function useRef$<T extends Element = Element>(externalRef?: React.Ref<T> | null): Ref$<T>

Creates an observable element ref. Can be used as a drop-in replacement for useRef, composed with callback refs, or used with forwardRef.

The element is wrapped with opaqueObject to prevent legendapp/state from making DOM properties reactive (deep observation).

@paramexternalRef - Optional. Accepts callback ref, RefObject, or null (forwardRef compatible).

@returnsA callable ref that is also observable via get/peek

@example

// standalone — useRef replacement
const el$ = useRef$<HTMLDivElement>();
return <div ref={el$} />;
// forwardRef compatible
const Component = forwardRef<HTMLDivElement>((props, ref) => {
const el$ = useRef$(ref);
return <div ref={el$} />;
});
// callback ref composition
const myRef = useCallback((node: HTMLDivElement | null) => {
node?.focus();
}, []);
const el$ = useRef$(myRef);
return <div ref={el$} />;

useRef$
,
import useElementBounding
useElementBounding
} from "@usels/core";
function
function Component(): React.JSX.Element
Component
() {
const
const el$: Ref$<HTMLDivElement>
el$
=
useRef$<HTMLDivElement>(externalRef?: React.Ref<HTMLDivElement> | undefined): Ref$<HTMLDivElement>

Creates an observable element ref. Can be used as a drop-in replacement for useRef, composed with callback refs, or used with forwardRef.

The element is wrapped with opaqueObject to prevent legendapp/state from making DOM properties reactive (deep observation).

@paramexternalRef - Optional. Accepts callback ref, RefObject, or null (forwardRef compatible).

@returnsA callable ref that is also observable via get/peek

@example

// standalone — useRef replacement
const el$ = useRef$<HTMLDivElement>();
return <div ref={el$} />;
// forwardRef compatible
const Component = forwardRef<HTMLDivElement>((props, ref) => {
const el$ = useRef$(ref);
return <div ref={el$} />;
});
// callback ref composition
const myRef = useCallback((node: HTMLDivElement | null) => {
node?.focus();
}, []);
const el$ = useRef$(myRef);
return <div ref={el$} />;

useRef$
<
interface HTMLDivElement
HTMLDivElement
>();
const {
const top$: any
top$
,
const left$: any
left$
,
const width$: any
width$
,
const height$: any
height$
} =
import useElementBounding
useElementBounding
(
const el$: Ref$<HTMLDivElement>
el$
);
return (
<
React.JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>
div
React.RefAttributes<HTMLDivElement>.ref?: React.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>
el$
}>
{
const width$: any
width$
.
any
get
()} × {
const height$: any
height$
.
any
get
()} at ({
const left$: any
left$
.
any
get
()}, {
const top$: any
top$
.
any
get
()})
</
React.JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>
div
>
);
}
const {
const top$: any
top$
,
const left$: any
left$
,
const update: any
update
} =
import useElementBounding
useElementBounding
(
const el$: Ref$<HTMLDivElement>
el$
);
// Force-recalculate bounding rect imperatively
const update: any
update
();
const { top$, left$ } = useElementBounding(el$, { windowScroll: false });

Skip requestAnimationFrame (synchronous reads)

Section titled “Skip requestAnimationFrame (synchronous reads)”
const { width$, height$ } = useElementBounding(el$, { useCssTransforms: false });
const { top$ } = useElementBounding(el$, { reset: false });
export interface UseElementBoundingOptions {
reset?: boolean;
windowResize?: boolean;
windowScroll?: boolean;
immediate?: boolean;
useCssTransforms?: boolean;
}
export interface UseElementBoundingReturn {
x$: Observable<number>;
y$: Observable<number>;
top$: Observable<number>;
right$: Observable<number>;
bottom$: Observable<number>;
left$: Observable<number>;
width$: Observable<number>;
height$: Observable<number>;
update: () => void;
}
export declare function useElementBounding(target: MaybeElement, options?: DeepMaybeObservable<UseElementBoundingOptions>): UseElementBoundingReturn;

View on GitHub

  • tigerwest
  • a7392ab 2026-03-06 - feat(core,browser): add sync strategy hooks (tigerwest)