All exports from @assistant-ui/store.
Hooks
useAui()
function useAui(): AssistantClient;Returns the store from the nearest AuiProvider. Does not re-render when scopes change — returns a stable reference.
useAui(scopes)
function useAui(scopes: useAui.Props): AssistantClient;Takes the store from the nearest AuiProvider and extends it by filling the provided scopes. Returns a new AssistantClient that includes both the parent's scopes and the newly provided ones.
type useAui.Props = {
[K in ClientNames]?: ClientElement<K> | DerivedElement<K>;
};useAuiState
function useAuiState<T>(selector: (state: AssistantState) => T): T;Subscribes to a slice of state. Re-renders only when the selected value changes (compared by Object.is). The selector must return a specific value — not the entire state object.
useAuiEvent
function useAuiEvent<TEvent extends AssistantEventName>(
selector: AssistantEventSelector<TEvent>,
callback: AssistantEventCallback<TEvent>,
): void;Subscribes to events. The selector can be a string ("scope.event") or an object ({ scope, event }). Unsubscribes on unmount.
Components
AuiProvider
<AuiProvider value={aui}>{children}</AuiProvider>Provides an AssistantClient to the React tree. Child components can access it via useAui().
AuiIf
<AuiIf condition={(s) => s.counter.count > 0}>
<ResetButton />
</AuiIf>Renders children only when the condition returns true. Uses useAuiState internally.
RenderChildrenWithAccessor
<RenderChildrenWithAccessor
getItemState={(aui) => aui.todoList().todo({ index }).getState()}
>
{(getItem) =>
children({
get todo() {
return getItem();
},
})
}
</RenderChildrenWithAccessor>Sets up a lazy item accessor for list rendering. The getItem function defers reading state until the consumer accesses it — if the children render function never reads the item, no subscription is created. When children returns a propless component (e.g. {() => <Todo />}), the output is automatically memoized.
| Prop | Type |
|---|---|
getItemState | (aui: AssistantClient) => T |
children | (getItem: () => T) => ReactNode |
See Rendering Lists for the full pattern.
Resource utilities
Derived
function Derived<K extends ClientNames>(config: Derived.Props<K>): DerivedElement<K>;Creates a derived scope that points to data in a parent scope.
// static meta
Derived({
source: "thread",
query: { index: 0 },
get: (aui) => aui.thread().message({ index: 0 }),
});
// dynamic meta
Derived({
getMeta: (aui) => ({ source: "thread", query: { index } }),
get: (aui) => aui.thread().message({ index }),
});The get function receives the current AssistantClient and must return the result of calling a parent scope method. It uses tapEffectEvent internally — always calls the latest closure.
attachTransformScopes
function attachTransformScopes<T extends (...args: any[]) => ResourceElement<any>>(
resource: T,
transform: (scopes: ScopesConfig, parent: AssistantClient) => ScopesConfig,
): void;Attaches a transform function to a resource. When the resource is mounted via useAui, the transform runs and can add or modify sibling scopes. Transforms are applied iteratively — new root scopes trigger their own transforms.
One transform per resource. Throws on duplicate.
Tap utilities
These are used inside Tap resources to integrate with Store.
tapClientResource
function tapClientResource<TMethods extends ClientMethods>(
element: ResourceElement<TMethods>,
): {
state: InferClientState<TMethods>;
methods: TMethods;
key: string | number | undefined;
};Wraps a single resource element into a client. Adds the client to the internal client stack for event scoping.
state is inferred from the element's getState() return type. If getState is not defined, state is undefined.
tapClientLookup
function tapClientLookup<TMethods extends ClientMethods>(
getElements: () => readonly ResourceElement<TMethods>[],
getElementsDeps: readonly unknown[],
): {
state: InferClientState<TMethods>[];
get: (lookup: { index: number } | { key: string }) => TMethods;
};Wraps a list of resource elements into clients. Each element must have a key (via withKey). Uses tapClientResource internally for each element.
get resolves a client by index or key. Throws if the lookup doesn't match.
tapClientList
function tapClientList<TData, TMethods extends ClientMethods>(
props: tapClientList.Props<TData, TMethods>,
): {
state: InferClientState<TMethods>[];
get: (lookup: { index: number } | { key: string }) => TMethods;
add: (data: TData) => void;
};Manages a dynamic list of clients with add/remove. Built on tapClientLookup.
type tapClientList.Props<TData, TMethods> = {
initialValues: TData[];
getKey: (data: TData) => string;
resource: ContravariantResource<TMethods, tapClientList.ResourceProps<TData>>;
};
type tapClientList.ResourceProps<TData> = {
key: string;
getInitialData: () => TData;
remove: () => void;
};getInitialData() is called once on mount. remove() removes the item from the list. Throws on duplicate key.
tapAssistantClientRef
function tapAssistantClientRef(): {
parent: AssistantClient;
current: AssistantClient | null;
};Returns a ref to the store being built. current is null during resource creation and populated after all sibling scopes are mounted. Use in tapEffect to access sibling scopes at runtime.
tapAssistantEmit
function tapAssistantEmit(): <TEvent extends Exclude<AssistantEventName, "*">>(
event: TEvent,
payload: AssistantEventPayload[TEvent],
) => void;Returns a stable emit function. Events are delivered via microtask — listeners fire after the current state update settles.
Types
ScopeRegistry
interface ScopeRegistry {}Module augmentation point. Augment this interface to register scopes:
declare module "@assistant-ui/store" {
interface ScopeRegistry {
counter: {
methods: {
getState: () => { count: number };
increment: () => void;
};
meta?: { source: ClientNames; query: Record<string, unknown> };
events?: { "counter.incremented": { newCount: number } };
};
}
}methods is required. meta and events are optional.
ClientOutput
type ClientOutput<K extends ClientNames> = ClientSchemas[K]["methods"] & ClientMethods;The return type for a resource implementing scope K. Use as the return type annotation on your resource function.
ClientNames
type ClientNames = keyof ClientSchemas;Union of all registered scope names.
AssistantClient
type AssistantClient = {
[K in ClientNames]: AssistantClientAccessor<K>;
} & {
subscribe(listener: () => void): Unsubscribe;
on<TEvent extends AssistantEventName>(
selector: AssistantEventSelector<TEvent>,
callback: AssistantEventCallback<TEvent>,
): Unsubscribe;
};The store object returned by useAui(). Each scope is an accessor. subscribe fires on any state change. on subscribes to typed events.
AssistantClientAccessor
type AssistantClientAccessor<K extends ClientNames> =
(() => ClientSchemas[K]["methods"]) &
(
| ClientMeta<K>
| { source: "root"; query: Record<string, never> }
| { source: null; query: null }
) &
{ name: K };A scope accessor. Call it (aui.counter()) to resolve the scope's methods. Read .source, .query, and .name for metadata.
AssistantState
type AssistantState = {
[K in ClientNames]: ClientSchemas[K]["methods"] extends {
getState: () => infer S;
}
? S
: never;
};The state object passed to useAuiState selectors. Each key is the return type of that scope's getState().
ClientMeta
type ClientMeta<K extends ClientNames> =
"meta" extends keyof ClientSchemas[K]
? Pick<ClientSchemas[K]["meta"], "source" | "query">
: never;The source and query shape for scope K, if meta is declared in ScopeRegistry.
ClientElement
type ClientElement<K extends ClientNames> = ResourceElement<ClientOutput<K>>;A resource element that implements scope K.
Unsubscribe
type Unsubscribe = () => void;Event types
AssistantEventName
type AssistantEventName = keyof AssistantEventPayload;Union of all registered event names, plus "*".
AssistantEventPayload
type AssistantEventPayload = ClientEventMap & {
"*": { [K in keyof ClientEventMap]: { event: K; payload: ClientEventMap[K] } }[keyof ClientEventMap];
};Maps event names to their payload types. The "*" key receives a wrapped { event, payload } object.
AssistantEventSelector
type AssistantEventSelector<TEvent extends AssistantEventName> =
| TEvent
| { scope: AssistantEventScope<TEvent>; event: TEvent };A string ("scope.event") or object ({ scope, event }). Strings default to scope matching the event's source.
AssistantEventScope
type AssistantEventScope<TEvent extends AssistantEventName> =
| "*"
| EventSource<TEvent>
| AncestorsOf<EventSource<TEvent>>;Valid scopes to listen at: the event's source scope, any ancestor of that scope, or "*" for all.
AssistantEventCallback
type AssistantEventCallback<TEvent extends AssistantEventName> = (
payload: AssistantEventPayload[TEvent],
) => void;normalizeEventSelector
function normalizeEventSelector<TEvent extends AssistantEventName>(
selector: AssistantEventSelector<TEvent>,
): { scope: AssistantEventScope<TEvent>; event: TEvent };Converts a string selector to { scope, event } form. Strings like "counter.incremented" become { scope: "counter", event: "counter.incremented" }.