carousel mounted by a tool call to search-products, holding shared state, hidden state, and a narration for the model:
views/carousel.tsx
useViewState, and narrating the screen with data-llm.
Decide Who Sees What
Every piece of state belongs to one of two shared channels, plus plain React for everything the model has no business knowing:
The model doesn’t watch the view live: it reads state when the conversation comes back to it, meaning on the next user message. Models can’t write the state directly. Each view instance owns its state; models typically see one instance per view, whichever had its state most recently updated.
ChatGPT pushes state updates to the model context depending on the display mode: updates from a PiP or fullscreen view are always pushed; updates from an inline view are pushed only while the instance has just mounted and no conversation turn has happened since.
Persist with useViewState
useViewState is useState with two superpowers:
- the host persists it: a closed and reopened conversation remounts the view instances with their previous state
- the model can read it: on the next conversation turn
views/carousel.tsx
cart is shared because the conversation needs it: the model reads it to recommend a matching item or answer “what’s in my cart?”. hovered stays in plain useState because a hover ends before the user’s next message: by the time the model could read it, the value is stale.
Narrate the Screen with data-llm
While useViewState pushes structured data to the model context, data-llm describes what the user currently sees in natural language. It’s recomputed on every render and synced to the model:
views/carousel.tsx
data-llm narration. Nest data-llm on inner elements and the model receives an indented outline of the screen.
Go Further
Register Tools
Define what humans and agents can do
Create Views
Craft interactive UIs rendered in conversation
Authenticate Users
Know who’s behind every tool call