Utility Hooks

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

useElementBounding

Tracks the bounding rectangle of an HTML element reactively. The returned object contains a reactive Cell for each dimension, and updates automatically 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

Provides a reactive Cell containing the current date and time, updated at a configurable 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 that track 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 network connection status reactively.

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 that is automatically synchronized with localStorage. Changes to the cell are persisted, and the stored value is restored on 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. Behaves identically to 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 value that may be either a static value or a Cell into a consistent Cell. If the input is already a Cell, the returned cell tracks it. If the input is a plain value, the returned cell holds it as a constant.

This is useful inside components that accept props which may or may not be reactive.

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 value that may be a static value, a Cell, or an AsyncCell into an AsyncDerivedCell. If the input is asynchronous, the returned cell resolves it through Retend's async derivation flow. If the input is synchronous, it tracks the cell or holds the static value as a constant.

This is useful inside async-aware components that accept props which may or may not already be reactive.

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 currently 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 reactively.

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>
  );
}