Guides
Surfaces
AI chat
An agent chat that runs on a scripted mock, with two interfaces between it and a real model and database.The chat surface is the interface of an AI assistant: a sidebar of chats
grouped into pinned, projects and recents, a conversation where agent steps and
widgets stream in, and a resizable context panel with the run timeline, the
widgets and the chat's settings. Below the desktop breakpoint the panels become
drawers.
It serves /chat and requires no slices. Chats live in localStorage and
answers come from a mock model in the browser, so it runs on zero environment
variables and no API key. Without the landing surface, / redirects to
/chat.
Everything the UI needs from outside goes through two interfaces in
src/lib/chat, and the hook that drives the UI, useChats, reads only them.
Where chats live is ChatStore, in store.ts:
It ships as localChatStore, seeded with sample chats. Keeping chats in a
database, per user or per organization, is a new object with this shape
assigned to chatStore in the same file.
Where answers come from is ChatModel, in model.ts:
stream yields ChatEvents: text deltas, steps (a step sent again with
the same id updates it), widgets, and a final usage with the tokens the
answer used. The surface ships mockModel, which plays canned agent runs
picked by what the message is about, and quickModel, the same without steps.
stream runs in the browser, so a real model is an API route that calls your
provider with its key, plus a ChatModel whose stream fetches that route and
turns the provider's stream into ChatEvents: text as deltas, tool calls as
steps, structured output as widgets. Add it to the models array in
model.ts and it appears in the model picker; the UI does not change. The
surface itself ships no route and no provider SDK.
Widgets are a closed union in types.ts: tasks, chart and sources. A
new kind is a new member there and a new case in
src/app/(chat)/components/Widgets.tsx.
The daily allowance in the usage meter is DAILY_TOKEN_LIMIT, a constant in
model.ts, and each model's rate says how much of it one token costs.
Every chat and project carries a ChatTheme: a brand, an accent and a neutral
from Once UI's own palette. ChatThemeScope applies it to the chat's subtree
as data-brand, data-accent and data-neutral, so every component inside
takes the chat's colors without a prop. A chat nobody has themed by hand
follows its project, including when it is moved into one; outside a project,
autoTheme picks a stable theme from the chat's id. Picking colors in the
theme dialog makes them the chat's own.
The chat building blocks in src/app/(chat)/components/kit are a copy of
Once UI core's chat components, kept here until the core release that exports
them is published.
Last updated October 18, 2026Edit this page on GitHub