Skip to main content
Between turns the model can’t see what the user does in your view. The data-llm attribute narrates the view’s current state to the model, so on the next turn it can answer about what is on screen.

Example

The carousel tells the model whether the shopper is browsing or looking at one product, so a follow-up like “is this one in stock?” resolves to the right item.
views/carousel.tsx
function Carousel({ products }: { products: Product[] }) {
  const [selected, setSelected] = useState<Product | null>(null);

  return (
    <div data-llm={selected ? `Viewing ${selected.name}` : "Browsing the catalog"}>
      {selected ? (
        <ProductDetails product={selected} />
      ) : (
        <ProductGrid products={products} onSelect={setSelected} />
      )}
    </div>
  );
}
Put it on any element, with a static string or an expression recomputed on render.

Behavior

  • Only the content currently on screen is sent. It is recomputed on every render and dropped when the element unmounts.
  • The text is model-visible, but not live: the host surfaces it to the model on the next turn, not mid-interaction.
  • Nested attributes form an indented outline. This:
<div data-llm="Reviewing the cart">
  <div data-llm={`${items.length} items, ${total}`}>{/* ... */}</div>
</div>
reaches the model as:
- Reviewing the cart
  - 3 items, $240
data-llm rides the same view state as useViewState, so its lifecycle governs when and where the model sees it.

Manage State

What to narrate, and what to leave out

useViewState

Persist structured state alongside the narration

useToolInfo

Read the tool result the view mounted with