Devtools shows the messages sent to the model, the tool input, and the tool result for a chat run.
The TanStack AI devtools panel shows you this while the app runs. It is a plugin for TanStack Devtools. This takes about 3 minutes.
npm i -D @tanstack/react-ai-devtools @tanstack/react-devtoolspnpm add -D @tanstack/react-ai-devtools @tanstack/react-devtoolsyarn add -D @tanstack/react-ai-devtools @tanstack/react-devtoolsbun add -d @tanstack/react-ai-devtools @tanstack/react-devtoolsRender TanStackDevtools with the AI plugin in your app root. Set connectToServerBus: true so the panel also gets events from the server.
React:
import { TanStackDevtools } from '@tanstack/react-devtools'
import { aiDevtoolsPlugin } from '@tanstack/react-ai-devtools'
const App = () => {
return (
<>
<TanStackDevtools
plugins={[aiDevtoolsPlugin()]}
eventBusConfig={{
connectToServerBus: true,
}}
/>
</>
)
}Svelte:
<script lang="ts">
import { aiDevtoolsPlugin } from '@tanstack/svelte-ai-devtools'
import { TanStackDevtools } from '@tanstack/svelte-devtools'
const plugins = [aiDevtoolsPlugin()]
</script>
<TanStackDevtools
{plugins}
eventBusConfig={{
connectToServerBus: true,
}}
/>If you already use other devtools plugins, add aiDevtoolsPlugin() to the same plugins array.
connectToServerBus: true needs a WebSocket/SSE server on port 4206. With Vite, @tanstack/devtools-vite starts it for you. Skip this step.
With Next.js or another bundler, start ServerEventBus when the server boots. In Next.js, put this in instrumentation.ts:
export async function register() {
if (
process.env['NEXT_RUNTIME'] === 'nodejs' &&
process.env.NODE_ENV === 'development'
) {
const { ServerEventBus } = await import(
'@tanstack/devtools-event-bus/server'
)
const bus = new ServerEventBus()
await bus.start()
}
}Start the app, open the devtools, and open the AI panel. Send a chat message. The panel lists your chat hook, and the timeline fills with the run, its events, and any tool calls.
Also there when you need it:
Events come from two places:
Each event has a source descriptor and a stable envelope id. The panel uses them to link related events and to skip duplicates.
The panel listens for active TanStack AI clients and shows them in the left sidebar.
Each hook entry shows its type, lifecycle, message count, run count, and the latest linked threadId. Select a hook to open its full timeline.
Chat hooks use a view by turn. A user message wraps every run and event that happened while the model answered that turn. The details view also shows small client and server state snapshots between runs, so you can see what changed.
When a page has more than one AI hook, give each one a label with devtools.name. The name changes only the label. Hook type, framework, thread id, and run links still come from the TanStack AI client.
import { fetchServerSentEvents, useChat } from '@tanstack/ai-react'
export function SupportChat() {
const chat = useChat({
threadId: 'support-chat',
connection: fetchServerSentEvents('/api/chat'),
devtools: {
name: 'Support Chat',
},
})
// render your chat UI with `chat.messages`, `chat.sendMessage`, etc.
}The same option works for the generation hooks:
import { fetchServerSentEvents, useGenerateImage } from '@tanstack/ai-react'
export function ImageStudio() {
const image = useGenerateImage({
threadId: 'generation-hooks:useGenerateImage',
connection: fetchServerSentEvents('/api/image'),
devtools: {
name: 'Image Studio',
},
})
// render your image generation UI with `image.generate` and `image.result`
}When a useChat hook gets tools, the panel lists the tools and their schemas. For inputs that use a Standard Schema, the panel shows a small form, so you can build a tool call payload without writing JSON by hand.
When you apply a fixture, the panel adds the tool call and its result to the real chat messages of that hook. The panel saves fixtures in browser localStorage, under the AI devtools namespace. They are there the next time you open the panel.
When a chat uses memoryMiddleware, the Memory tab of the hook shows what the server memory backend did for that conversation. The tab groups the data by scope (session):
Memory runs on the server. Its state gets to the panel over the chat stream, as a CUSTOM event that the client sends again, the same way generation results do.