Skip to content

Generative UI

Map AI tool calls to UI component descriptors declaratively. When a model calls get_weather, your UI renders a <WeatherCard> instead of raw JSON.

Setup

typescript
import { createUIRegistry } from '@store-ai/core';

const registry = createUIRegistry();

Registering Renderers

typescript
registry.register('get_weather', (toolCall) => ({
  component: 'WeatherCard',
  props: {
    city: toolCall.input?.['city'],
    forecast: toolCall.output,
  },
  loading: toolCall.status === 'pending' || toolCall.status === 'partial',
  toolCall,
}));

registry.register('search', (toolCall) => ({
  component: 'SearchResults',
  props: { query: toolCall.input?.['query'], results: toolCall.output },
  loading: toolCall.status !== 'complete',
  toolCall,
}));

Fallback Renderer

Handle unregistered tools with a catch-all:

typescript
registry.setFallback((toolCall) => ({
  component: 'GenericToolCall',
  props: { name: toolCall.name, input: toolCall.input, output: toolCall.output },
  loading: toolCall.status !== 'complete',
  toolCall,
}));

Connecting to a Store

typescript
import { connectUI } from '@store-ai/core';

const { getElements, destroy } = connectUI(store, registry);

// Reactive — updates when store.toolCalls changes
const elements = getElements();
// → [{ component: 'WeatherCard', props: { city: 'SF' }, loading: true, toolCall: ... }]

Framework Integration

React

tsx
function ToolCallRenderer({ store, registry }) {
  const toolCalls = useAIToolCalls(store);
  const elements = registry.renderAll(toolCalls);

  return elements.map((el) => {
    switch (el.component) {
      case 'WeatherCard':
        return <WeatherCard key={el.toolCall.id} {...el.props} loading={el.loading} />;
      case 'SearchResults':
        return <SearchResults key={el.toolCall.id} {...el.props} />;
      default:
        return <pre key={el.toolCall.id}>{JSON.stringify(el.props)}</pre>;
    }
  });
}

UIElement Shape

typescript
interface UIElement {
  component: string; // component identifier
  props: Record<string, unknown>; // props for the component
  loading: boolean; // true while tool call is in progress
  toolCall: ToolCallState; // raw tool call data
}

API

MethodDescription
registry.register(name, renderer)Register a renderer for a tool name
registry.unregister(name)Remove a renderer
registry.has(name)Check if registered
registry.render(toolCall)Render one tool call (or null)
registry.renderAll(toolCalls)Render all, skipping unregistered (unless fallback set)
registry.setFallback(renderer)Set catch-all for unregistered tools
registry.list()List registered tool names
connectUI(store, registry)Reactive connection, returns { getElements, destroy }

Released under the MIT License.