TanStack AI
Use @durable-streams/tanstack-ai-transport when you want TanStack AI chat sessions with resumability, resilience, and built-in support for multi-tab, multi-device, and multi-user sharing.
This transport writes chat session state into Durable Streams and reads it back through a durable connection, so subscribers can resume from offsets and keep multiple clients attached to the same session state. It plugs into TanStack AI's streaming connection adapter layer.
Install
pnpm add @durable-streams/tanstack-ai-transport @durable-streams/clientClient
Create a durable TanStack connection:
import { durableStreamConnection } from "@durable-streams/tanstack-ai-transport"
const connection = durableStreamConnection({
sendUrl: "/api/chat?id=chat_123",
readUrl: "/api/chat-stream?id=chat_123",
initialOffset: undefined,
})Pass this connection to useChat from @tanstack/ai-react.
Server
Write model chunks to a durable chat session stream:
import { toDurableChatSessionResponse } from "@durable-streams/tanstack-ai-transport"
return toDurableChatSessionResponse({
stream: {
writeUrl,
headers,
},
newMessages: [latestUserMessage],
responseStream,
})This appends the new user message, pipes TanStack AI chunks into the durable stream, and returns an empty success response.
Recommended chat session flow
1. Client connection
const connection = durableStreamConnection({
sendUrl: `/api/chat?id=${chatId}`,
readUrl: `/api/chat-stream?id=${encodeURIComponent(chatId)}`,
initialOffset: resumeOffsetFromSSR,
})Use it with useChat, following the same pattern as TanStack AI's connection adapters:
useChat({ id: chatId, connection, live: true })2. POST route
Your POST /api/chat route should:
- validate the chat id
- build a durable stream write URL
- keep
newMessagesexplicit, usually just the latest prompt - start the model
responseStream - return
toDurableChatSessionResponse(...)
3. GET proxy route
Your GET /api/chat-stream route should:
- accept a chat
id - build the upstream durable read URL on the durable stream server
- forward read query params like
offsetandlive - add durable stream server-side read auth headers
- return the upstream body and headers
4. SSR hydrate and resume
For page loaders, use:
const { messages, offset } = await materializeSnapshotFromDurableStream({
readUrl,
headers,
})Then send messages and offset to the client, and use that offset as initialOffset when creating the durable connection.
This avoids replaying the entire session during first subscribe.