API Reference

All exports from @assistant-ui/tap.

resource

Creates a resource factory function.

import { resource } from "@assistant-ui/tap";

const Counter = resource((props: { initialValue: number }) => {
  const [count, setCount] = tapState(props.initialValue);
  return { count, increment: () => setCount((c) => c + 1) };
});

Returns a factory function — calling it produces a ResourceElement.

withKey

Attaches a key to a ResourceElement for identity preservation in lists.

import { withKey } from "@assistant-ui/tap";

withKey("my-key", Counter({ initialValue: 0 }))

Hooks

All hooks follow the rules of hooks.

tapState

const [value, setValue] = tapState(initialValue);
const [value, setValue] = tapState(() => expensiveDefault());

Local state. setValue accepts a value or an updater function (prev) => next.

tapReducer

const [state, dispatch] = tapReducer(reducer, initialState);
const [state, dispatch] = tapReducer(reducer, initialArg, init);

State management with a reducer function. dispatch has a stable identity. Bails out (no re-render) when the reducer returns the same state via Object.is.

tapReducerWithDerivedState

This API is experimental, intended for advanced use cases, and may change.

const [state, dispatch] = tapReducerWithDerivedState(reducer, getDerivedState, initialState);
const [state, dispatch] = tapReducerWithDerivedState(reducer, getDerivedState, initialArg, init);

Like tapReducer, but accepts a getDerivedState function as the second argument. During each render pass, getDerivedState(state) is called once after processing any queued actions. If it returns a new value (by Object.is), the derived value becomes the current state and triggers a re-render. The return type is inferred from getDerivedState, allowing you to narrow or extend the state type.

tapEffect

tapEffect(() => {
  // side effect
  return () => { /* cleanup */ };
}, [deps]);

Runs after commit. Without a dependency array, runs after every render.

tapMemo

const value = tapMemo(() => compute(a, b), [a, b]);

Memoizes a value. Recomputes when dependencies change.

tapCallback

const fn = tapCallback(() => { /* ... */ }, [deps]);

Memoizes a function. Shorthand for tapMemo(() => fn, deps).

tapRef

const ref = tapRef(initialValue);
ref.current; // read/write

A mutable ref that persists across renders.

tapConst

const value = tapConst(() => new EventEmitter(), []);

Computed once on mount, never recomputed. The second argument must always be [].

tapEffectEvent

const handler = tapEffectEvent((msg: string) => {
  // always has access to latest closure
});

Returns a stable function reference that always calls the latest version of the callback. Useful for event handlers passed to effects.


Composition hooks

tapResource

const value = tapResource(Counter({ initialValue: 0 }));
const value = tapResource(Counter({ initialValue }), [initialValue]);

Renders a single child resource. Returns the child's return value.

The optional dependency array controls when new props are applied.

The dependency array API is experimental and may change.

tapResources

This API is experimental and may change.

const values = tapResources(
  () => items.map((item) => withKey(item.id, TodoItem({ text: item.text }))),
  [items],
);

Renders a dynamic list of child resources. Every element must have a key via withKey.

tapResourceRoot

const handle = tapResourceRoot(Counter({ initialValue: 0 }));

handle.getValue();  // current value
handle.subscribe(() => { /* on change */ });

Renders a child as a subscribable store. The parent doesn't re-render when the child updates.


Imperative API

createResourceRoot

Creates a resource instance outside of React.

import { createResourceRoot } from "@assistant-ui/tap";

const root = createResourceRoot();
const handle = root.render(Counter({ initialValue: 0 }));

Root methods:

MethodDescription
root.render(element)Render (or re-render) with a resource element. Returns a subscribable handle.
root.unmount()Unmount and clean up

Subscribable handle methods (returned by root.render()):

MethodDescription
handle.getValue()Read the current return value
handle.subscribe(callback)Subscribe to changes

flushResourcesSync

Synchronously flushes pending tap scheduler updates.

import { flushResourcesSync } from "@assistant-ui/tap";

flushResourcesSync(() => {
  handle.getValue().increment();
});
// state is already updated here

Only applies to tap-scheduled trees (createResourceRoot / tapResourceRoot). For useResource trees, use flushSync from react-dom.


Context

The context API is experimental and may change significantly.

createResourceContext

import { createResourceContext } from "@assistant-ui/tap";

const ThemeContext = createResourceContext("light");

Creates a context with a default value.

tap

import { tap } from "@assistant-ui/tap";

const theme = tap(ThemeContext);

Reads the current value of a context inside a resource.

withContextProvider

import { withContextProvider } from "@assistant-ui/tap";

const child = withContextProvider(ThemeContext, "dark", () => {
  return tapResource(Button());
});

Provides a context value to all resources rendered within the callback.


React

useResource

import { useResource } from "@assistant-ui/tap/react";

const { count, increment } = useResource(Counter({ initialValue: 0 }));

Binds a resource to a React component's lifecycle. The component re-renders when the resource's state changes.


Types

Resource

type Resource<R, P> = (props: P) => ResourceElement<R, P>;

The factory function returned by resource().

ResourceElement

type ResourceElement<R, P> = { type: Resource<R, P>; props: P; key?: string | number };

A lightweight { type, props } description of a resource to render.

ContravariantResource

type ContravariantResource<R, P> = (props: P) => ResourceElement<R>;

A contravariant version of Resource for type-level flexibility when accepting resources as parameters. The difference from Resource is that the returned ResourceElement omits the P type parameter, allowing broader assignability.