Skip to content

useMutationObserver

Reactive wrapper around the MutationObserver API. Observes one or more DOM nodes for mutations — attribute changes, child additions/removals, and text content changes. Targets can be Ref$, MaybeElement, or a plain Element.

status: observingdata-active: children: 0
DOM
Observer
— no mutations recorded yet
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 useMutationObserver
useMutationObserver
} 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
>();
import useMutationObserver
useMutationObserver
(
const el$: Ref$<HTMLDivElement>
el$
,
(
records: any
records
) => {
records: any
records
.
any
forEach
((
r: any
r
) => {
/* handle r.type, r.target */
});
},
{
attributes: boolean
attributes
: true,
childList: boolean
childList
: true }
);
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$
} />;
}
useMutationObserver(el$, callback, { attributes: true });

Only fire when aria-expanded or data-active change:

useMutationObserver(el$, callback, {
attributes: true,
attributeFilter: ["aria-expanded", "data-active"],
});
useMutationObserver(
el$,
(records) => {
records.forEach((r) => {
const next = (r.target as Element).getAttribute(r.attributeName!);
console.log("old:", r.oldValue, "→ new:", next);
});
},
{ attributes: true, attributeOldValue: true }
);
useMutationObserver(el$, callback, { childList: true, subtree: true });
useMutationObserver([el$, anotherEl], callback, { attributes: true });
const { stop, resume } = useMutationObserver(el$, callback, { childList: true });
stop(); // disconnects the observer
resume(); // reconnects with the same target and options
const { takeRecords } = useMutationObserver(el$, callback, { attributes: true });
const pending = takeRecords();
const { isSupported$ } = useMutationObserver(el$, callback, { attributes: true });
console.log(isSupported$.get()); // Observable<boolean>
export interface UseMutationObserverOptions extends MutationObserverInit {
}
export interface UseMutationObserverReturn {
isSupported$: Observable<boolean>;
stop: () => void;
resume: () => void;
takeRecords: () => MutationRecord[];
}
export declare function useMutationObserver(target: MaybeElement | MaybeElement[], callback: MutationCallback, options?: UseMutationObserverOptions): UseMutationObserverReturn;

View on GitHub

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