Build a chatbot
Put a React chat UI on top of your chat endpoint.
Put a chat UI in front of your agent with the AI SDK useChat hook. Blazing Agents keeps the history, so the page only remembers the session ID.
Before you begin
Finish Connect Blazing Agents to your app. This page talks to that /api/chat endpoint, which works the same whether you wrote it in TypeScript or Python.
You need a React app served from the same origin as the endpoint, for example through your dev server's proxy. Install the chat hook:
npm install @blazingagents/sdk ai @ai-sdk/reactAdd the chat component
BlazingAgentsChatTransport connects useChat to your endpoint. It sends only the newest message, picks up the new session ID from the first response, and sends that ID back on every later message.
import { useChat } from "@ai-sdk/react";
import { BlazingAgentsChatTransport } from "@blazingagents/sdk";
import { useState } from "react";
export function Chat({ token }: { token: string }) {
const [input, setInput] = useState("");
const [transport] = useState(
() =>
new BlazingAgentsChatTransport({
api: "/api/chat",
headers: { authorization: `Bearer ${token}` },
sessionId: localStorage.getItem("chat-session") ?? undefined,
onSessionId: (id) => localStorage.setItem("chat-session", id),
}),
);
const { messages, sendMessage, status } = useChat({ transport });
return (
<main>
{messages.map((message) => (
<p key={message.id}>
<strong>{message.role}:</strong>{" "}
{message.parts.map((part) => (part.type === "text" ? part.text : ""))}
</p>
))}
<form
onSubmit={(event) => {
event.preventDefault();
void sendMessage({ text: input });
setInput("");
}}
>
<input value={input} onChange={(event) => setInput(event.target.value)} />
<button type="submit" disabled={status !== "ready"}>Send</button>
</form>
</main>
);
}Render <Chat token={...} /> with your user's sign-in token. With the demo endpoint from the previous page, that is the value of DEMO_USER_TOKEN. Send a message and the answer streams in. Reload the page and your next message continues the same conversation, because the session ID is in localStorage.
What to add next
The component above is the smallest chat that works. Add the pieces your product needs, by hand or by asking your coding agent.
- Stop. Call
stop()fromuseChatto cancel a turn. A cancelled turn adds nothing to the saved history. It cannot undo a tool call that already ran, and a resend can run that tool again. - Roll back failed turns. A turn that fails or is stopped is not saved by Blazing Agents, but
useChatstill shows it. Keep the messages from the last successfulonFinishand restore them withsetMessages()on error or stop. Leave the typed text in the input so the user can edit it and send again. Each send is a new attempt with a new message ID. There is no separate retry call. - Show errors. Render
errorfromuseChatand callclearError()before the next send. - Regenerate. Call
regenerate()to replace the last saved answer. If it fails or is stopped, the old answer stays. Enable it only after a successful answer, because a prompt that failed was never saved. - New conversation. Remove
chat-sessionfromlocalStorage, create a new transport without asessionId, and clear the messages. The next message starts a new session. - History after a reload. The component shows only the messages from the current page visit. Have your backend read earlier messages with
client.sessions.messages()and pass them touseChatasmessages. See reading the transcript. - Dropped connections. If the network drops after the agent finished, the answer may already be saved. Reload history before assuming it was lost.
- Busy sessions. While a tool call in the session waits for approval, new messages return a
session_busyerror. Show it and let the user send again later. If two messages race in the same session, one of them fails rather than mixing the histories. - Images. Send them in the same message as extra file parts. See send an image.
- Tool approvals. They arrive in the same stream when an agent needs a person to allow a tool call. Send the decision through your backend, not only with
addToolApprovalResponse. See render the resumed answer.
Next
- Examples for complete Next.js, TanStack Start, Vite + Hono, Express, and FastAPI apps built this way.
- Sessions and turns for history, busy sessions, and regeneration.
- Choose what to build next