Generative UI

Let the model compose cards, tables, charts, and form controls drawn with React Native components.

The model can answer with an interface built from the generative UI vocabulary: cards, text, tables, charts, form controls, and more, the same components Generative UI documents for the web. On React Native, Metro resolves @assistant-ui/generative-ui/spec to a React Native build through the react-native export condition, so toSpecCatalog draws every component with React Native's own components and no other dependency. createSpecToolkit from generative-frame/spec/assistant-ui resolves the same way and renders the model's render_spec calls in your thread.

Install

npx expo install @assistant-ui/generative-ui generative-frame zod

@assistant-ui/generative-ui takes react-native as an optional peer, and generative-frame also takes @assistant-ui/react-native; your app already has both.

Add it to your runtime

The toolkit runs render_spec in the app, so its entries go into Tools({ toolkit }), and the toolkit's spec module carries the guidance the model needs to write specs for this catalog:

app/runtime-provider.tsx
import {
  AuiConfig,
  AssistantRuntimeProvider,
  Tools,
  useAssistantInstructions,
  type AssistantRuntime,
} from "@assistant-ui/react-native";
import { toSpecCatalog } from "@assistant-ui/generative-ui/spec";
import { createSpecToolkit } from "generative-frame/spec/assistant-ui";
import { Thread } from "@/components/assistant-ui/elements/thread.aui";

const { catalog, components } = toSpecCatalog();
const spec = createSpecToolkit(catalog, { components });
const tools = Tools({
  toolkit: spec.entries({ execution: "frontend", display: "standalone" }),
});
const guidance = (spec.modules ?? [])
  .map((module) => module.guidance())
  .join("\n\n");

function SpecInstructions() {
  useAssistantInstructions(guidance);
  return null;
}

export function RuntimeProvider({ runtime }: { runtime: AssistantRuntime }) {
  const config = AuiConfig({ tools });

  return (
    <AssistantRuntimeProvider runtime={runtime} config={config}>
      <SpecInstructions />
      <Thread />
    </AssistantRuntimeProvider>
  );
}

Your route forwards the toolkit's tools to the model, as for any frontend tool. To run render_spec on the server instead, see frontend and backend execution.

On a phone

Every component dispatches the same events with the same payloads as on the web, writes a value bound with $bindState back the same way, and an asForm Card or a Form collects its named controls when a submit button or a field's return key submits it. The default styles follow the device's light or dark mode, and text on a Card with its own background turns white.

A few components take a form that suits a touch screen:

  • Select and RadioGroup are single-choice lists, and CheckboxGroup is a list of checkboxes.
  • Slider is a stepper with buttons on either side of its value.
  • DatePicker is a text field with a format hint.
  • Chart draws every chart kind as bars.
  • Icon is a text character.
  • Carousel pages through its cards in a horizontal scroll view.
  • background takes a color React Native can paint, and a CSS gradient leaves the plain surface. A Box takes points, pixels, rems, or percentages.

Change a component

defaultGenerativeUILibrary from @assistant-ui/generative-ui/spec is the React Native vocabulary. Replace an entry to restyle a component, or add your own, as on the web:

app/catalog.tsx
import { Text } from "react-native";
import { z } from "zod";
import {
  defaultGenerativeUILibrary,
  toSpecCatalog,
} from "@assistant-ui/generative-ui/spec";

export const { catalog, components } = toSpecCatalog({
  ...defaultGenerativeUILibrary,
  Rating: {
    description: "A rating from 1 to 5 stars.",
    properties: z.object({ value: z.number().int().min(1).max(5) }),
    render: ({ value }) => <Text>{"★".repeat(value)}</Text>,
  },
});