TanStack AI bindings for the Octane UI framework.
This package ports @tanstack/ai-react@0.17.0 onto Octane while reusing
@tanstack/ai and @tanstack/ai-client unchanged. The runtime export surface
matches the React adapter, so migration starts by changing the package import:
// before
import { useChat } from '@tanstack/ai-react'
// after
import { useChat } from '@octanejs/tanstack-ai'The renderer-bearing hook modules are authored as .tsrx and compiled by
Octane. Matching .tsrx.d.ts companions are checked declaration emits of
those implementations, preserving the complete generic surface for
TypeScript consumers.
pnpm add @octanejs/tanstack-ai @tanstack/ai @tanstack/ai-client octaneimport { useState } from 'octane'
import { useChat } from '@octanejs/tanstack-ai'
export function Chat() @{
const [input, setInput] = useState('')
const chat = useChat({
fetcher: myFetcher,
})
<div>
@for (const message of chat.messages; key message.id) {
<p>
{(message.role +
': ' +
message.parts
.map((part) => (part.type === 'text' ? part.content : ''))
.join('')) as string}
</p>
}
<input
value={input}
onInput={(event) => setInput(event.currentTarget.value)}
/>
<button
onClick={() => {
void chat.sendMessage(input)
setInput('')
}}
>
Send
</button>
</div>
}useChat has no input state of its own — hold the text box value in a local
useState and pass it to sendMessage. Note the onInput handler: Octane
drives text controls per keystroke through the native input event, not a
synthetic onChange.
The adapter includes useChat, useRealtimeChat, useMcpAppBridge,
useGeneration, useGenerateImage, useGenerateAudio, useGenerateSpeech,
useGenerateVideo, useTranscription, useSummarize, and
useAudioRecorder. It also re-exports all 30 @tanstack/ai-client
convenience helpers and types (fetchServerSentEvents, fetchHttpStream,
xhrServerSentEvents, xhrHttpStream, stream, rpcStream,
createChatClientOptions, createMcpAppBridge, and their associated types)
unchanged, mirroring the upstream @tanstack/ai-react index.
Server rendering through octane/server is supported. useChat renders its
initial message snapshot without browser-only setup.
- The
./mcp-appssubpath and itsMCPAppResourcecomponent are not ported: they renderAppRendererfrom the React-only@mcp-ui/client, which has no Octane equivalent. The framework-agnosticuseMcpAppBridgehook is ported and available on the main entry. - Octane uses native events: text/file/recorder inputs drive updates via
onInput; there is no syntheticonChangelayer. - Octane has no StrictMode double-invoke and always provides
useId, so no random-id fallback is needed. - Realtime reconnects and token refreshes use the latest
getTokenand adapter supplied to the hook; upstream captures the first render's callbacks. - The declared realtime
onStatusChangecallback is invoked alongside the hook's state update; upstream 0.17.0 currently drops the external callback. - One upstream
useChattest case ("auto-resume on mount / when the browser comes back online") is omitted: it targetsChatClient.prototype.maybeAutoResume, an API absent from the pinned (and latest published)@tanstack/ai-client@0.21.0and never invoked byuseChat. It is untestable in this binding until that dependency ships the method.
The port runs TanStack AI's React adapter tests against Octane across all
eleven hooks, with no skipped, todo, or expected-failure cases (except the
untestable auto-resume case above). A differential test compiles a shared
chat fixture for Octane and React and compares streamed output after each
step; output is byte-equal against real @tanstack/ai-react@0.17.0. An SSR
fixture and the upstream compile-time type tests are also included.
Current scope and verification status are tracked in the generated
bindings status table, sourced from this
package's status.json.
MIT — contains source derived from TanStack AI (MIT), adapted for Octane.