Skip to main content
A view loses its local React state when the host remounts it, and the model never sees what the view is showing. useViewState is a drop-in useState that fixes both.

Example

A carousel persists the shopper’s sort choice across remounts, and the model sees it on the next turn.
import { useViewState } from "skybridge/web";

type CarouselState = { sort: "newest" | "price" };

function Carousel() {
  // shared with the model, survives remounts
  const [state, setState] = useViewState<CarouselState>({ sort: "newest" });

  return (
    <div>
      <button onClick={() => setState({ sort: "newest" })}>Newest</button>
      <button onClick={() => setState({ sort: "price" })}>Price</button>
      <ProductGrid sort={state.sort} />
    </div>
  );
}

Type Parameters

T

T extends Record<string, unknown>;
The shape of the persisted state. T is inferred from defaultState; set it explicitly only when defaultState is omitted or null.

Parameters

defaultState

defaultState?: T | (() => T | null) | null;
The initial state, used only when the host holds no persisted state for this view. Pass a value or a lazy initializer that runs once on mount. Pass one and state is never null; omit it, or pass null, and state is null until the first write. When the host already has persisted state, that value wins and defaultState is ignored.

Returns

useViewState returns a readonly [state, setState] tuple, the same ergonomics as useState.

state

state: T | null;
The current persisted state, model-visible: the host exposes it as structured content on the next turn. The Skybridge-internal context key written by DataLLM is stripped before it reaches your component.

setState

setState(state: SetStateAction<T>): void;
Updates the state and persists it on the host. Accepts a value or an updater function (prev) => next, exactly like React’s useState setter. When defaultState is omitted the type widens to SetStateAction<T | null>, so the setter also accepts null. The new value is written through to the host right away, then reflected back into state on the next render.

Lifecycle

Persistence. View state belongs to a single view instance, the view one tool call rendered. It survives that instance’s re-renders, remounts, and display-mode changes, and a closed and reopened conversation remounts the instance with its previous state. A different tool invocation gets its own state, starting from defaultState. Model visibility. When a view has several instances, the model typically sees just one, whichever updated its state most recently. On ChatGPT, a PiP or fullscreen view always pushes updates to the model, but an inline view pushes only right after it mounts, before any conversation turn, so an inline view’s later updates may not reach the model. Durability. View state is best-effort, not durable storage. On Claude, older state can be evicted as other views accumulate, so a view may reopen to its defaultState.

Manage State

Decide what to persist and share with the model

DataLLM

Narrate the on-screen state to the model

useToolInfo

Read the tool result the view mounted with