Install Store and connect your first Tap resource to React.
Store (@assistant-ui/store) connects Tap resources to React with scoped, type-safe state management.
Installation
npm install @assistant-ui/store @assistant-ui/tapDefine your scope types
Register your scopes by augmenting the ScopeRegistry interface. This gives you type safety across all Store hooks.
import "@assistant-ui/store";
declare module "@assistant-ui/store" {
interface ScopeRegistry {
counter: {
methods: {
getState: () => { count: number };
increment: () => void;
};
};
}
}Create a resource
Define a Tap resource that returns ClientOutput<"counter">. This connects the resource's methods to the scope type you just registered.
import { resource } from "@assistant-ui/tap";
import { useState, useMemo } from "react";
import type { ClientOutput } from "@assistant-ui/store";
const useCounterResource = ({
initialCount = 0,
}: {
initialCount?: number;
}): ClientOutput<"counter"> => {
const [count, setCount] = useState(initialCount);
const state = useMemo(() => ({ count }), [count]);
return {
getState: () => state,
increment: () => setCount((c) => c + 1),
};
};
export const CounterResource = resource(useCounterResource);Use it in React
Use AuiProvider to create a store and provide it to the tree, and useAuiState to subscribe to state.
"use client";
import { AuiConfig, AuiProvider } from "@assistant-ui/store";
import { CounterResource } from "@/lib/store/counter-store";
import { CounterDisplay } from "./CounterDisplay";
export const CounterApp = () => {
const config = AuiConfig({ counter: CounterResource({ initialCount: 0 }) });
return (
<AuiProvider config={config}>
<CounterDisplay />
</AuiProvider>
);
};"use client";
import { useAui, useAuiState } from "@assistant-ui/store";
export const CounterDisplay = () => {
const count = useAuiState((s) => s.counter.count);
const aui = useAui();
return (
<div>
<p>Count: {count}</p>
<button onClick={() => aui.counter.increment()}>+</button>
</div>
);
};useAuiState((s) => s.counter.count) subscribes to just the count value, so the component only re-renders when it changes.
aui.counter exposes the scope's methods, so aui.counter.increment() calls the increment method you defined in the resource.