Utility Hooks

All hooks listed on this page are imported from retend-utils/hooks.

useElementBounding

Tracks an HTML element's bounding rectangle. The returned object contains a reactive Cell for each dimension and updates when the element's size or position changes.

Parameters:

  • elementRef — A Cell<HTMLElement | null> referencing the element to track.
  • options (optional):
    • reset (boolean, default true) — Reset values when the element leaves the tree.
    • windowResize (boolean, default true) — Update on window resize.
    • windowScroll (boolean, default true) — Update on window scroll.
    • updateTiming ('sync' | 'next-frame', default 'sync') — Timing strategy for updates.

Returns: An object with reactive cells: width, height, x, y, top, right, bottom, left.

import { Cell } from 'retend';
import { useElementBounding } from 'retend-utils/hooks';

function PositionTracker() {
  const trackedElement = Cell.source(null);
  const bounds = useElementBounding(trackedElement);

  return (
    <>
      <div ref={trackedElement} style="width: 100px; height: 50px;">
        Track me!
      </div>
      <p>
        Position: X={bounds.x}, Y={bounds.y}
      </p>
      <p>
        Size: {bounds.width} × {bounds.height}
      </p>
    </>
  );
}

useLiveDate

Returns a reactive Cell containing the current date and time. The cell updates at the specified interval.

Parameters:

  • interval (number, default 1000) — Update frequency in milliseconds.

Returns: Cell<Date>

import { Cell } from 'retend';
import { useLiveDate } from 'retend-utils/hooks';

function Clock() {
  const now = useLiveDate(1000);
  const timeString = Cell.derived(() => now.get().toLocaleTimeString());

  return <p>Current time: {timeString}</p>;
}

useWindowSize

Returns reactive cells for the current window dimensions.

Parameters: None.

Returns: { width: Cell<number>, height: Cell<number> }

import { Cell } from 'retend';
import { If } from 'retend';
import { useWindowSize } from 'retend-utils/hooks';

function AdaptiveLayout() {
  const { width } = useWindowSize();
  const isMobile = Cell.derived(() => width.get() < 768);

  return If(isMobile, {
    true: () => <div>Mobile layout</div>,
    false: () => <div>Desktop layout</div>,
  });
}

useOnlineStatus

Tracks the browser's network connection status.

Parameters: None.

Returns: Cell<boolean>true when the browser is online.

import { If } from 'retend';
import { useOnlineStatus } from 'retend-utils/hooks';

function NetworkBanner() {
  const isOnline = useOnlineStatus();

  return (
    <p>
      {If(isOnline, {
        true: () => 'You are online.',
        false: () => 'You are offline.',
      })}
    </p>
  );
}

useLocalStorage

Creates a reactive Cell synchronized with localStorage. Changes to the cell are persisted, and the stored value is restored during initialization.

Parameters:

  • key (string) — The localStorage key.
  • initialValue (JSON-serializable) — Default value if the key does not exist.

Returns: Cell containing the stored value.

import { useLocalStorage } from 'retend-utils/hooks';

function ThemeSwitcher() {
  const theme = useLocalStorage('theme', 'light');

  const toggleTheme = () => {
    theme.set(theme.get() === 'light' ? 'dark' : 'light');
  };

  return (
    <>
      <button onClick={toggleTheme}>Toggle Theme</button>
      <p>Current theme: {theme}</p>
    </>
  );
}

useSessionStorage

Creates a reactive Cell synchronized with sessionStorage. It has the same behavior as useLocalStorage, but the data is scoped to the browser session.

Parameters:

  • key (string) — The sessionStorage key.
  • initialValue (JSON-serializable) — Default value if the key does not exist.

Returns: Cell containing the stored value.

import { useSessionStorage } from 'retend-utils/hooks';

function SessionCounter() {
  const count = useSessionStorage('count', 0);

  const increment = () => {
    count.set(count.get() + 1);
  };

  return (
    <>
      <button onClick={increment}>Increment</button>
      <p>Count: {count}</p>
    </>
  );
}

useDerivedValue

Normalizes a static value or Cell to a Cell. If the input is a Cell, the returned cell tracks it. If the input is a plain value, the returned cell holds it as a constant.

Use this in components that accept either reactive or non-reactive props.

Parameters:

  • property (Cell<T> | T) — The value or cell to derive from.

Returns: Cell<T>

import { Cell } from 'retend';
import { useDerivedValue } from 'retend-utils/hooks';

function Label(props: { text: Cell<string> | string }) {
  const text = useDerivedValue(props.text);

  return <span>{text}</span>;
}

useDerivedAsyncValue

Normalizes a static value, Cell, or AsyncCell to an AsyncDerivedCell. For asynchronous input, the returned cell resolves it through Retend's async derivation flow. For synchronous input, it tracks the cell or holds the static value as a constant.

Use this in components that accept synchronous or asynchronous reactive props.

Parameters:

  • property (AsyncCell<T> | Cell<T> | T) — The value or cell to derive from.

Returns: AsyncDerivedCell<T>

import { Cell, type AsyncCell } from 'retend';
import { useDerivedAsyncValue } from 'retend-utils/hooks';

function AsyncLabel(props: {
  text: AsyncCell<string> | Cell<string> | string;
}) {
  const text = useDerivedAsyncValue(props.text);

  return <span>{text}</span>;
}

useMatchMedia

Creates a reactive Cell that tracks whether a CSS media query matches.

Parameters:

  • query (string) — A valid media query string (e.g. '(prefers-color-scheme: dark)').

Returns: Cell<boolean>

import { If } from 'retend';
import { useMatchMedia } from 'retend-utils/hooks';

function ThemeIndicator() {
  const prefersDark = useMatchMedia('(prefers-color-scheme: dark)');

  return If(prefersDark, {
    true: () => <p>Dark mode is active.</p>,
    false: () => <p>Light mode is active.</p>,
  });
}

useCursorPosition

Tracks the cursor position within the window.

Parameters: None.

Returns: { x: Cell<number>, y: Cell<number> }

import { useCursorPosition } from 'retend-utils/hooks';

function CursorTracker() {
  const { x, y } = useCursorPosition();

  return (
    <p>
      Cursor: ({x}, {y})
    </p>
  );
}