@get-asset/react
Chat
Styled, drop-in chat with the bookkeeping agent — a card holding a titled header over a scrolling transcript, above a composer pinned to the bottom. Owner messages sit right as bubbles; replies read as prose on the left, with their source, follow-up prompts, and feedback controls stacked underneath. Replies stream in token by token and the thread keeps itself scrolled to the newest one. Enter sends, Shift+Enter adds a newline, and the send button becomes a stop button while a reply is generating. Covers loading, error, empty, and success.
Built on the headless Chat compound from @get-asset/sdk.
Preview
Loading preview…
Required scopes
The access token used by AssetProvider must include these scopes for this primitive to fetch data successfully:
chat:createchat:readchat:update
API reference
Root props
| Prop | Type | Default | Description |
|---|---|---|---|
chatId | string | — | An existing conversation to continue. Its transcript is fetched on mount, and swapping it for another id opens that one in place. Omit to start fresh — the chat is created on the first message. |
prompt | string | — | Send a message supplied by another input. Each non-empty prompt is sent once; clear the prop before sending the same prompt again. |
greeting | string | "Good afternoon, {business}" | Heading shown above an empty transcript. Defaults to a time-of-day greeting addressed to the business. |
subtitle | string | — | Sentence under the greeting explaining what the agent can answer. |
thinking | string | "Reading your books…" | Line shown beside the animated dots while a reply is being composed. |
placeholder | string | "Ask anything about your business…" | Composer placeholder. |
disclaimer | string | "Answers are generated from your books…" | Small print under the composer, once a conversation has started. |
title | string | — | The conversation's title, shown in the header. Defaults to the opening question; pass a string to own it yourself. |
history | ChatHistoryEntry[] | — | Past conversations for the side panel. Fetched from the API by default; pass [] to skip the request. |
defaultHistoryOpen | boolean | false | Whether the history panel starts open. |
onSelectChat | (chatId: string) => void | — | Called when a past conversation is picked. It opens in place, so this is a notification — useful for mirroring the choice into a URL. |
onBack | () => void | — | Notified when the header's back arrow is used. Back already returns to the resting view on its own; supply this to also navigate or update a URL. |
onExport | (messages: ChatMessage[], title: string) => void | — | Called by the header's download control. Defaults to downloading the transcript as markdown. |
onGraphExport | (graph: ChatGraph) => void | — | A headless hook for exporting a graph's data (CSV by default via the SDK's GraphExport slot). AssetChat's own download control exports the whole graph card as an SVG image instead, so it doesn't call this. |
logo | ReactNode | — | Mark shown above the greeting on the resting view — typically the business's logo. |
onAttach | () => void | — | Provide to show an attach control in the composer. The chat API accepts text only, so the upload is yours to own. |
resume | boolean | true | Rejoin a turn that was still streaming when the page reloaded, or one still running in a conversation opened from the history panel. |
onChatCreated | (chatId: string) => void | — | Called with the new chat's id the first time a chat is created — persist it to reopen the conversation later. |
onFinish | (message: ChatMessage) => void | — | Called with the finished assistant message at the end of each turn. |
messages | ChatMessage[] | — | Override the transcript (skips the API call). |
state | "loading" | "error" | "empty" | "success" | "thinking" | "streaming" | "turn-error" | — | Force a specific view for testing. |
className | string | — | Forwarded to the outer Card wrapper element. |
States
Loading preview…
Loading preview…
Loading preview…
Loading preview…
Loading preview…
Loading preview…
Loading preview…
Loading preview…
Loading preview…
Anatomy of a reply
Replies are markdown — paragraphs, bold section leads, bullet lists, and inline code, which is how figures reach the page (`$63,880` renders as a value rather than prose). Anything outside that set falls through as plain text.
Everything else around the answer is optional. A sourceline under the answer comes from the reply’s metadata; the follow-up prompts beneath it come from its suggestions, and clicking one sends it as the next question.
Graphs
For a comparison question the agent may answer with a graph as well as prose. It renders as a card under the answer: the title and summary, the chart with its legend, and the provenance line beneath. A bar graph compares categories (a label above each bar); a linegraph tracks each series across an ordered axis. The card picks the shape from the graph’s type — you drop in one AssetChat either way.
Graphs are persisted on the reply, so a conversation reopened from history draws them again. The download beside the title saves the whole card as an SVG image — title, chart, legend, and footnote, composed from the live layout with the theme’s colours inlined so it stands alone. Colours come from the --asset-chart-series-*tokens — a fill and outline per series for bars, a saturated linestroke for lines, assigned by recency so the most recent series leads in purple (with the area gradient on lines) and earlier ones fall to the muted colours — so a chart takes the host’s palette like everything else.
Forecasts
A forward-looking answer may carry a forecast card. A projection charts one scenario over time: booked history draws as a solid line, the projection continues it dashed from “today”, and the P10–P90 likely range is shaded behind — for a cash forecast a $0 line marks the floor to watch. A comparisondraws a line per scenario over the projected periods only, the base one emphasised — no history/projection split, since scenarios are identical over the past. The card picks its shape from the forecast’s type; you drop in one AssetChat either way.
Beneath the chart, “See details” reveals the forecast’s readiness— a confidence badge, a plain-English assessment, and, when the agent needs input, tappable question chips that send an answer back into the forecast — and its drivers, the assumptions the number was built from, formatted by kind (a ratio as a percentage, days as days, the rest as amounts). The download beside the title saves the whole card as an SVG, the same as a graph.
Header
The header holds a back arrow, the conversation’s title, a download, and the history toggle. Every part bar the toggle renders only once there’s a conversation, so the resting view keeps the bare toggle. The back arrow returns to that resting view — a fresh conversation — with no wiring; pass onBack only if you also want to navigate or update a URL. The title is read-only.
Sizing
The chat is a Cardlike every other component, so it takes the same border, radius, and background from the theme. Unlike the others it fills the height it is given and sits at that height throughout — the resting view is already full height, so opening a conversation fills the transcript in and scrolls rather than resizing the card. Give the parent a height (a fixed-height box, a flex child, or a grid cell); with no height to fill it falls back to a 420px minimum and grows with its content.
Mobile
When the chat is wide the history panel sits beside the conversation. Once it narrows past 720px — a small screen, or the component dropped into a narrow column — opening history takes over the whole card as an overlay instead of a cramped side panel, with its own close button (the header’s history toggle is covered while it’s open). It’s a container query on the chat’s own width, not the viewport, so a narrow embed on a wide page behaves the same as a phone.