本页还没有译文
本页 47 段还没译成Português (BR),下面显示的是英文原文。@tanstack/ai-react
还没翻译译自英文(tanstack.com)本页 47 段都还没译
React hooks for TanStack AI, providing convenient React bindings for the headless client.
For a typed headless chat UI, see React Chat UI.
For React Native, the documented support surface is narrow: useChat with chat
connection adapters. React DOM-specific UI packages and TanStack AI devtools UI
are not part of the React Native support surface.
For a complete native journey, see Quick Start: React Native.
Installation
useRegisterWebMCPTools(tools, options?)
Register executable client tools after the React component mounts. React removes them on cleanup and replaces them when tools or options change.
For a complete setup and behavior guide, see WebMCP Tools.
import {
useRegisterWebMCPTools,
type UseRegisterWebMCPToolsOptions,
} from "@tanstack/ai-react";
import { searchProducts } from "./tools";
const tools = [searchProducts];
const options: UseRegisterWebMCPToolsOptions<typeof tools> = {
onError(error) {
console.error(error);
},
};
function ProductsPage() {
useRegisterWebMCPTools(tools, options);
return null;
}UseRegisterWebMCPToolsOptions<TTools, TContext> contains toolOptions, context, and onError. The hook owns the registration signal.
The context field is required when a tool declares a required runtime context. Keep tools and options stable when their values do not change.
usePageWebMCPTools(options?)
Read the WebMCP tools on the page as client tools. The array starts empty and updates when the page adds or removes a tool. Pass it to useChat as tools.
import { usePageWebMCPTools } from "@tanstack/ai-react";
export function useSameOriginPageTools() {
return usePageWebMCPTools({
filter: (tool) => tool.origin === location.origin,
});
}filter skips a tool when it returns false. onError gets a failed WebMCP read. For a complete guide, see Page WebMCP Tools in Chat.
createChatHook(options)
Bind chatOptions once at module scope. Call useChat() in the screen to create the instance. Per-call overrides may set threadId, initialMessages, live, and forwardedProps. They must not change tools, interrupts, or outputSchema.
import { createChatHook, fetchServerSentEvents } from "@tanstack/ai-react";
const chatOptions = {
connection: fetchServerSentEvents("/api/chat"),
};
const { useChat } = createChatHook(chatOptions);
function ChatScreen() {
const chat = useChat({ threadId: "support-1" });
return null;
}useChat(chatOptions) from this package still works when you want to pass the full object at the call site. Rename the bound hook if both are in one file: const { useChat: useSupportChat } = createChatHook(chatOptions).
useChat(options?)
Main hook for managing chat state in React with full type safety.
import { useChat, fetchServerSentEvents } from "@tanstack/ai-react";
import {
createChatClientOptions,
type InferChatMessages
} from "@tanstack/ai-client";
import { toolDefinition } from "@tanstack/ai";
import { z } from "zod";
import { useState } from "react";
const updateUIDef = toolDefinition({
name: "updateUI",
description: "Update the UI with a notification",
inputSchema: z.object({
message: z.string(),
}),
outputSchema: z.object({ success: z.boolean() }),
});
function ChatComponent() {
const [notification, setNotification] = useState<string | null>(null);
// Create client tool implementations
const updateUI = updateUIDef.client((input) => {
setNotification(input.message);
return { success: true };
});
// Create typed tools array (no 'as const' needed!)
const tools = [updateUI];
const chatOptions = createChatClientOptions({
connection: fetchServerSentEvents("/api/chat"),
tools,
});
// Fully typed messages!
type ChatMessages = InferChatMessages<typeof chatOptions>;
const { messages, sendMessage, isLoading, error, addToolApprovalResponse } =
useChat(chatOptions);
return <div>{/* Chat UI with typed messages */}</div>;
}Options
Extends ChatClientOptions from @tanstack/ai-client:
connection- Connection adapter (required)tools?- Array of client tool implementations (with.client()method)initialMessages?- Initial messages arraythreadId?- The only identity for this chat. Required when persistence is on. If omitted, minted after mount.forwardedProps?- Arbitrary client-controlled JSON forwarded to the server in the AG-UIRunAgentInput.forwardedPropsfield (e.g.,{ provider: 'openai', model: 'gpt-5.5' })body?- Deprecated. UseforwardedPropsinstead. Still works for backward compatibility; values are merged intoforwardedPropson the wirebyok?- Optional BYOK keyring fromdefineByok. On each send the client prepares the resolved provider and stampsx-byok-*request headers. Keys never go in the bodybyokProvider?- Optional function that returns the provider slug for this chat. If it returns a slug, only that key is prepared and sent. Otherwise the mergedproviderfromforwardedProps,body, and per-callsendMessagebodyis used. Later sources win. If no slug resolves, the send throws instead of attaching every stored keycontext?- Typed client-local runtime context passed to client tool implementations. This value is not serialized to the serveronResponse?- Callback when response is receivedonChunk?- Callback when stream chunk is receivedonFinish?- Callback when response finishesonError?- Callback when error occursonInterruptStateChange?- Callback when interrupt state changes; context source ishydratefor restored state andlivefor streamed or client-initiated updatesstreamProcessor?- Stream processing configuration
Note: Client tools are now automatically executed - no onToolCall callback needed!
Returns
import type { UIMessage } from "@tanstack/ai-react";
import type { ModelMessage } from "@tanstack/ai";
import type {
MultimodalContent,
SendMessageOptions,
} from "@tanstack/ai-client";
interface UseChatReturn {
messages: UIMessage[];
sendMessage: (
content: string | MultimodalContent,
options?: SendMessageOptions,
) => Promise<void>;
append: (message: ModelMessage | UIMessage) => Promise<void>;
addToolResult: (result: {
toolCallId: string;
tool: string;
output: any;
state?: "output-available" | "output-error";
errorText?: string;
}) => Promise<void>;
addToolApprovalResponse: (response: {
id: string;
approved: boolean;
}) => Promise<void>;
reload: () => Promise<void>;
stop: () => void;
isLoading: boolean;
isHydrating: boolean;
error: Error | undefined;
setMessages: (messages: UIMessage[]) => void;
clear: () => void;
}useByok(client)
Subscribe to a ByokClient snapshot in React.
import { useByok } from "@tanstack/ai-react";
import { byok } from "./byok";
export function KeyStatus() {
const snapshot = useByok(byok);
const openai = snapshot.status.openai;
const last4 = openai && "masked" in openai ? openai.masked : "No key";
return <p>{last4}</p>;
}snapshot has status, locked, and prompt. Call byok.update(provider, value) from your own UI to save a key. See Bring Your Own Key.
Connection Adapters
Re-exported from @tanstack/ai-client for convenience:
import {
fetchServerSentEvents,
fetchHttpStream,
fetchJson,
xhrServerSentEvents,
xhrHttpStream,
stream,
type ConnectionAdapter,
type FetchConnectionOptions,
type FetchJsonOptions,
type XhrConnectionOptions,
} from "@tanstack/ai-react";For React Native or Expo chat screens, use an absolute server URL and prefer
xhrHttpStream() with a server route that returns toHttpResponse(). Use
xhrServerSentEvents() with toServerSentEventsResponse() when you want SSE.
Use fetchHttpStream() only when the runtime supports streaming fetch,
Response.body.getReader(), and TextDecoder; otherwise it throws
UnsupportedResponseStreamError.
If the host cannot stream at all, use fetchJson() with a server route that
returns toJsonResponse(). See JSON.
XHR adapter options include headers, withCredentials, signal, body, and
xhrFactory. Fetch adapter options include headers, credentials, signal,
body, and fetchClient. fetchJson() also takes pollIntervalMs. Both option objects may be provided directly or as a
function that resolves per request.
For error narrowing, import UnsupportedResponseStreamError and
StreamTruncatedError from @tanstack/ai-client.
Example: Basic Chat
import { useState } from "react";
import { useChat, fetchServerSentEvents } from "@tanstack/ai-react";
export function Chat() {
const [input, setInput] = useState("");
const { messages, sendMessage, isLoading } = useChat({
connection: fetchServerSentEvents("/api/chat"),
});
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
if (input.trim() && !isLoading) {
sendMessage(input);
setInput("");
}
};
return (
<div>
<div>
{messages.map((message) => (
<div key={message.id}>
<strong>{message.role}:</strong>
{message.parts.map((part, idx) => {
if (part.type === "thinking") {
return (
<div key={idx} className="text-sm text-gray-500 italic">
💭 Thinking: {part.content}
</div>
);
}
if (part.type === "text") {
return <span key={idx}>{part.content}</span>;
}
return null;
})}
</div>
))}
</div>
<form onSubmit={handleSubmit}>
<input
value={input}
onChange={(e) => setInput(e.target.value)}
disabled={isLoading}
/>
<button type="submit" disabled={isLoading}>
Send
</button>
</form>
</div>
);
}Example: Tool Approval
import { useChat, fetchServerSentEvents } from "@tanstack/ai-react";
export function ChatWithApproval() {
const { messages, sendMessage, addToolApprovalResponse } = useChat({
connection: fetchServerSentEvents("/api/chat"),
});
return (
<div>
{messages.map((message) =>
message.parts.map((part) => {
if (
part.type === "tool-call" &&
part.state === "approval-requested" &&
part.approval
) {
return (
<div key={part.id}>
<p>Approve: {part.name}</p>
<button
onClick={() =>
addToolApprovalResponse({
id: part.approval!.id,
approved: true,
})
}
>
Approve
</button>
<button
onClick={() =>
addToolApprovalResponse({
id: part.approval!.id,
approved: false,
})
}
>
Deny
</button>
</div>
);
}
return null;
})
)}
</div>
);
}Example: Client Tools with Type Safety
import { useChat, fetchServerSentEvents } from "@tanstack/ai-react";
import {
createChatClientOptions,
type InferChatMessages
} from "@tanstack/ai-client";
import { toolDefinition } from "@tanstack/ai";
import { z } from "zod";
import { useState } from "react";
const updateUIDef = toolDefinition({
name: "updateUI",
description: "Update the UI with a notification",
inputSchema: z.object({
message: z.string(),
type: z.string(),
}),
outputSchema: z.object({ success: z.boolean() }),
});
const saveToStorageDef = toolDefinition({
name: "saveToStorage",
description: "Save a value to storage",
inputSchema: z.object({
key: z.string(),
value: z.string(),
}),
outputSchema: z.object({ saved: z.boolean() }),
});
export function ChatWithClientTools() {
const [notification, setNotification] = useState<{ message: string; type: string } | null>(null);
// Create client implementations
const updateUI = updateUIDef.client((input) => {
// ✅ input is fully typed!
setNotification({ message: input.message, type: input.type });
return { success: true };
});
const saveToStorage = saveToStorageDef.client((input) => {
localStorage.setItem(input.key, input.value);
return { saved: true };
});
// Create typed tools array (no 'as const' needed!)
const tools = [updateUI, saveToStorage];
const { messages, sendMessage } = useChat({
connection: fetchServerSentEvents("/api/chat"),
tools, // ✅ Automatic execution, full type safety
});
return (
<div>
{messages.map((message) =>
message.parts.map((part) => {
if (part.type === "tool-call" && part.name === "updateUI") {
// ✅ part.input and part.output are fully typed!
return <div key={part.id}>Tool executed: {part.name}</div>;
}
return null;
})
)}
</div>
);
}createChatClientOptions(options)
Helper to create typed chat options (re-exported from @tanstack/ai-client).
import {
createChatClientOptions,
fetchServerSentEvents,
type InferChatMessages
} from "@tanstack/ai-client";
import { tool1, tool2 } from "./tools";
// Create typed tools array (no 'as const' needed!)
const tools = [tool1, tool2];
const chatOptions = createChatClientOptions({
connection: fetchServerSentEvents("/api/chat"),
tools,
});
type Messages = InferChatMessages<typeof chatOptions>;Types
Re-exported from @tanstack/ai-client:
UIMessage<TTools>- Message type with tool type parameterMessagePart<TTools>- Message part with tool type parameterTextPart- Text content partThinkingPart- Thinking content partToolCallPart<TTools>- Tool call part (discriminated union)ToolResultPart- Tool result partChatClientOptions<TTools, TContext>- Chat client options with typed client runtime contextConnectionAdapter- Connection adapter interfaceInferChatMessages<T>- Extract message type from options
Re-exported from @tanstack/ai:
toolDefinition()- Create isomorphic tool definitionToolDefinitionInstance- Tool definition typeClientTool- Client tool typeServerTool- Server tool type
Next Steps
- Getting Started - Learn the basics
- Bring Your Own Key - Store keys and pass
byokintouseChat - Tools Guide - Learn about the isomorphic tool system
- Client Tools - Learn about client-side tools