React SDK Hooks
Complete reference for @tambo-ai/react hooks - thread management, messaging, suggestions, and component state.
The @tambo-ai/react package provides hooks for building AI-powered applications with explicit thread management and React Query integration.
useTambo
The primary hook for the React SDK. Provides access to thread state, messages, streaming status, and registry functions.
import { useTambo } from "@tambo-ai/react";
function Chat() {
const {
// Thread state
thread,
messages,
currentThreadId,
// Streaming status
streamingState,
isStreaming,
isWaiting,
isIdle,
// Authentication
authState,
isIdentified,
// Thread management
initThread,
switchThread,
startNewThread,
// Run control
cancelRun,
// Registry
registerComponent,
registerTool,
registerTools,
componentList,
toolRegistry,
// API client
client,
dispatch,
} = useTambo();
}Return Values
Thread State
| Value | Type | Description |
|---|---|---|
thread | ThreadState | undefined | Current thread state including messages and metadata |
messages | TamboThreadMessage[] | Array of messages in the current thread with computed properties |
currentThreadId | string | ID of the currently active thread |
Streaming Status
| Value | Type | Description |
|---|---|---|
streamingState | StreamingState | Detailed streaming state including status, runId, error |
isStreaming | boolean | true when actively streaming a response |
isWaiting | boolean | true when waiting for server response |
isIdle | boolean | true when no generation is in progress |
Authentication
| Value | Type | Description |
|---|---|---|
authState | TamboAuthState | Current authentication state as a discriminated union |
isIdentified | boolean | Shorthand for authState.status === "identified" |
Run Control
| Value | Type | Description |
|---|---|---|
cancelRun | () => Promise<void> | Cancel the active run. No-op if there's no active run or thread is a placeholder. |
Thread Management
| Value | Type | Description |
|---|---|---|
initThread | (threadId: string, initialThread?: Partial<TamboThread>) => void | Initialize a thread with optional initial state |
switchThread | (threadId: string) => void | Switch to a different thread by ID |
startNewThread | () => string | Create a new thread and return its ID |
updateThreadName | (threadId: string, name: string) => Promise<void> | Update a thread's display name |
Registry
| Value | Type | Description |
|---|---|---|
registerComponent | (component: TamboComponent) => void | Register a component dynamically |
registerTool | (tool: TamboTool) => void | Register a single tool |
registerTools | (tools: TamboTool[]) => void | Register multiple tools |
componentList | ComponentRegistry | Map of registered components |
toolRegistry | Map<string, TamboTool> | Map of registered tools |
Example: Basic Chat
import { useTambo, useTamboThreadInput } from "@tambo-ai/react";
function ChatInterface() {
const { messages, isStreaming, startNewThread } = useTambo();
const { value, setValue, submit } = useTamboThreadInput();
return (
<div>
<button onClick={() => startNewThread()}>New Chat</button>
<div className="messages">
{messages.map((msg) => (
<MessageBubble key={msg.id} message={msg} />
))}
{isStreaming && <LoadingIndicator />}
</div>
<input
value={value}
onChange={(e) => setValue(e.target.value)}
onKeyDown={(e) => e.key === "Enter" && !e.shiftKey && submit()}
disabled={isStreaming}
/>
</div>
);
}useTamboThreadInput
Provides shared input state management across components. Input state is shared via the TamboThreadInputProvider, allowing features like suggestions to update the input directly.
import { useTamboThreadInput } from "@tambo-ai/react";
function MessageInput() {
const {
value,
setValue,
submit,
images,
addImage,
addImages,
removeImage,
clearImages,
threadId,
isPending,
isDisabled,
isError,
error,
} = useTamboThreadInput();
}Return Values
| Value | Type | Description |
|---|---|---|
value | string | Current input value |
setValue | Dispatch<SetStateAction<string>> | Update the input value |
submit | (options?: SubmitOptions) => Promise<{ threadId: string | undefined }> | Submit the current input (see SubmitOptions) |
images | StagedImage[] | Array of staged images |
addImage | (file: File) => Promise<void> | Add a single image |
addImages | (files: File[]) => Promise<void> | Add multiple images |
removeImage | (id: string) => void | Remove a staged image |
clearImages | () => void | Clear all staged images |
threadId | string | undefined | Current thread ID |
isPending | boolean | true while submission is in progress |
isDisabled | boolean | true when input should be disabled (pending submission or not authenticated) |
isError | boolean | true if submission failed |
error | Error | null | Error details if submission failed |
Example: Input with Images
function RichInput() {
const { value, setValue, submit, images, addImage, removeImage, isPending } =
useTamboThreadInput();
const handleFileChange = async (e: React.ChangeEvent<HTMLInputElement>) => {
const files = Array.from(e.target.files || []);
for (const file of files) {
await addImage(file);
}
};
return (
<div>
<div className="staged-images">
{images.map((img) => (
<div key={img.id}>
<img src={img.dataUrl} alt={img.name} />
<button onClick={() => removeImage(img.id)}>Remove</button>
</div>
))}
</div>
<input type="file" accept="image/*" onChange={handleFileChange} />
<textarea
value={value}
onChange={(e) => setValue(e.target.value)}
disabled={isPending}
/>
<button onClick={() => submit()} disabled={isPending || !value.trim()}>
Send
</button>
</div>
);
}useTamboThread
React Query hook to fetch a single thread by ID.
import { useTamboThread } from "@tambo-ai/react";
function ThreadLoader({ threadId }: { threadId: string }) {
const { data, isLoading, isError, error } = useTamboThread(threadId);
if (isLoading) return <Loading />;
if (isError) return <Error message={error.message} />;
return <ThreadView thread={data} />;
}Parameters
| Parameter | Type | Description |
|---|---|---|
threadId | string | The thread ID to fetch |
options | UseQueryOptions | Optional React Query options |
Return Values
Returns a standard React Query UseQueryResult<ThreadRetrieveResponse>.
The query has a staleTime of 1000ms by default for real-time data freshness.
Auth-gating
This hook automatically disables the query until the user is identified
(authState.status === "identified"). While gated, React Query keeps the
query disabled/idle. You can add additional enabled logic via the options
parameter — both conditions must be true for the query to run.
useTamboThreadList
React Query hook to list threads for the current user.
import { useTamboThreadList } from "@tambo-ai/react";
function ThreadSidebar() {
const { data, isLoading } = useTamboThreadList();
if (isLoading) return <Loading />;
return (
<ul>
{data?.threads.map((thread) => (
<li key={thread.id}>{thread.name || "Untitled"}</li>
))}
</ul>
);
}Parameters
| Parameter | Type | Description |
|---|---|---|
listOptions | ThreadListOptions | Optional filtering options |
queryOptions | UseQueryOptions | Optional React Query options |
ThreadListOptions
| Property | Type | Description |
|---|---|---|
userKey | string | User key (defaults to provider context) |
limit | number | Maximum threads to return |
cursor | string | Pagination cursor |
Return Values
Returns a standard React Query UseQueryResult<ThreadListResponse>.
The query has a staleTime of 5000ms by default.
Auth-gating
Like useTamboThread, this hook automatically disables the query until the
user is identified. You can add additional enabled logic via queryOptions
— both conditions must be true for the query to run.
Automatic userKey merging
You don't need to pass userKey in listOptions — the hook automatically
uses the userKey from TamboProvider. If you do pass one explicitly, it
takes precedence over the provider value.
useTamboComponentState
Bidirectional state synchronization between component and server. Similar to React's useState, but syncs state changes with the Tambo server.
import { useTamboComponentState } from "@tambo-ai/react";
function Counter() {
const [count, setCount, { isPending, error, flush }] = useTamboComponentState(
"count",
0,
);
return (
<div>
<span>{count}</span>
<button onClick={() => setCount((c) => c + 1)} disabled={isPending}>
Increment
</button>
</div>
);
}Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
keyName | string | Required | Unique key identifying this state |
initialValue | S | Required | Initial state value |
debounceTime | number | 500 | Milliseconds to debounce server updates |
Return Values
Returns a tuple: [currentState, setState, meta]
| Value | Type | Description |
|---|---|---|
currentState | S | Current state value |
setState | (newState: S | ((prev: S) => S)) => void | State setter (supports functional updates) |
meta.isPending | boolean | true while syncing with server |
meta.error | Error | null | Error if sync failed |
meta.flush | () => void | Immediately flush pending updates |
This hook only works within components rendered via ComponentRenderer or
components that are part of the message rendering pipeline.
Example: Editable Card
function EditableCard({ title: initialTitle }: { title: string }) {
const [title, setTitle, { isPending }] = useTamboComponentState(
"title",
initialTitle,
300, // debounce 300ms
);
return (
<div>
<input
value={title}
onChange={(e) => setTitle(e.target.value)}
className={isPending ? "saving" : ""}
/>
{isPending && <span>Saving...</span>}
</div>
);
}useTamboStreamStatus
Track streaming status for components rendered via ComponentRenderer. Provides both overall stream status and per-prop granularity.
Important: Props update repeatedly during streaming and may be partial. Use propStatus.<field>?.isSuccess before treating a prop as complete.
import { useTamboStreamStatus } from "@tambo-ai/react";
function MyComponent(props: MyProps) {
const { streamStatus, propStatus } = useTamboStreamStatus<MyProps>();
if (streamStatus.isPending) return <Skeleton />;
return (
<div>
<h2 className={propStatus.title?.isStreaming ? "animate-pulse" : ""}>
{props.title}
</h2>
{streamStatus.isSuccess && <p>Stream complete</p>}
</div>
);
}Return Values
| Value | Type | Description |
|---|---|---|
streamStatus | StreamStatus | Overall streaming status for the component |
propStatus | Partial<Record<keyof Props, PropStatus>> | Per-prop streaming status. Keys are absent until the prop receives its first token. See PropStatus. |
This hook only works inside components rendered via ComponentRenderer. The
renderer provides the necessary context for tracking streaming state.
useTamboSuggestions
Generate and manage AI-powered message suggestions.
import { useTamboSuggestions } from "@tambo-ai/react";
function SuggestionChips() {
const {
suggestions,
accept,
generate,
isLoading,
isGenerating,
isAccepting,
} = useTamboSuggestions();
return (
<div>
{suggestions.map((suggestion) => (
<button
key={suggestion.id}
onClick={() => accept({ suggestion, shouldSubmit: true })}
disabled={isAccepting}
>
{suggestion.text}
</button>
))}
<button onClick={() => generate()} disabled={isGenerating}>
Refresh Suggestions
</button>
</div>
);
}Options
const result = useTamboSuggestions({
maxSuggestions: 3, // 1-10, default 3
autoGenerate: true, // auto-generate on mount, default true
queryOptions: {}, // React Query options
});Return Values
| Value | Type | Description |
|---|---|---|
data | SuggestionsQueryResponse | undefined | Raw query response |
suggestions | Suggestion[] | Array of suggestion objects |
isLoading | boolean | Initial loading state |
isSuccess | boolean | Query succeeded |
isError | boolean | Query failed |
error | Error | null | Query error |
isFetching | boolean | Fetching (includes background refetch) |
generate | () => Promise<SuggestionCreateResponse | undefined> | Generate new suggestions |
isGenerating | boolean | Generation in progress |
generateError | Error | null | Generation error |
accept | (options: AcceptSuggestionOptions) => Promise<void> | Accept a suggestion |
isAccepting | boolean | Accept in progress |
acceptError | Error | null | Accept error |
selectedSuggestionId | string | null | Currently selected suggestion |
AcceptSuggestionOptions
| Property | Type | Description |
|---|---|---|
suggestion | Suggestion | The suggestion to accept |
shouldSubmit | boolean | Submit immediately after accepting |
useTamboInteractable
Manages the registry of interactable components — components already placed in your UI that the AI can update. Provides functions to add, remove, update, query, and select interactable components.
import { useTamboInteractable } from "@tambo-ai/react";
function InteractableManager() {
const {
interactableComponents,
addInteractableComponent,
removeInteractableComponent,
updateInteractableComponentProps,
getInteractableComponent,
getInteractableComponentsByName,
clearAllInteractableComponents,
setInteractableState,
getInteractableComponentState,
setInteractableSelected,
clearInteractableSelections,
} = useTamboInteractable();
}Return Values
| Value | Type | Description |
|---|---|---|
interactableComponents | TamboInteractableComponent[] | All registered interactable components |
addInteractableComponent | (component: Omit<TamboInteractableComponent, "id" | "createdAt">) => string | Register a component, returns its generated ID |
removeInteractableComponent | (id: string) => void | Remove a component by ID |
updateInteractableComponentProps | (id: string, newProps: Record<string, unknown>) => string | Partially update a component's props |
getInteractableComponent | (id: string) => TamboInteractableComponent | undefined | Get a component by ID |
getInteractableComponentsByName | (name: string) => TamboInteractableComponent[] | Get all components matching a name |
clearAllInteractableComponents | () => void | Remove all interactable components |
setInteractableState | (componentId: string, key: string, value: unknown) => void | Set a single state key on a component |
getInteractableComponentState | (componentId: string) => Record<string, unknown> | undefined | Get a component's state |
setInteractableSelected | (componentId: string, isSelected: boolean) => void | Mark a component as selected for AI interaction |
clearInteractableSelections | () => void | Clear all component selections |
Most applications use
withTamboInteractable()instead of calling this hook directly. The HOC handles registration and cleanup automatically.
useCurrentInteractablesSnapshot
Returns a cloned snapshot of all currently registered interactable components. The snapshot is safe to mutate without affecting internal state.
import { useCurrentInteractablesSnapshot } from "@tambo-ai/react";
function InteractablesList() {
const interactables = useCurrentInteractablesSnapshot();
return (
<ul>
{interactables.map((item) => (
<li key={item.id}>{item.name}</li>
))}
</ul>
);
}Returns: TamboInteractableComponent[] (shallow-cloned array with cloned props)
useTamboContextAttachment
Manages context attachments — pieces of context that are automatically included with the next user message and then cleared.
import { useTamboContextAttachment } from "@tambo-ai/react";
function FileAttacher() {
const {
attachments,
addContextAttachment,
removeContextAttachment,
clearContextAttachments,
} = useTamboContextAttachment();
const attachFile = (fileContent: string, fileName: string) => {
addContextAttachment({
context: fileContent,
displayName: fileName,
type: "file",
});
};
}Return Values
| Value | Type | Description |
|---|---|---|
attachments | ContextAttachment[] | Current context attachments |
addContextAttachment | (attachment: Omit<ContextAttachment, "id">) => ContextAttachment | Add an attachment, returns it with its ID |
removeContextAttachment | (id: string) => void | Remove an attachment by ID |
clearContextAttachments | () => void | Remove all attachments |
ContextAttachment
| Property | Type | Description |
|---|---|---|
id | string | Unique identifier (auto-generated) |
context | string | The context value included in additionalContext |
displayName | string | Optional display name for UI rendering |
type | string | Optional type for grouping (e.g., "file") |
useTamboContextHelpers
Accesses the context helpers system for managing dynamic additional context that is included with every message.
import { useEffect } from "react";
import { useTamboContextHelpers } from "@tambo-ai/react";
function ContextManager() {
const { addContextHelper, removeContextHelper } = useTamboContextHelpers();
useEffect(() => {
addContextHelper("appState", () => ({
currentPage: window.location.pathname,
theme: "dark",
}));
return () => removeContextHelper("appState");
}, [addContextHelper, removeContextHelper]);
}Return Values
| Value | Type | Description |
|---|---|---|
getAdditionalContext | () => Promise<AdditionalContext[]> | Resolve all helpers into additional context entries |
getContextHelpers | () => ContextHelpers | Get the current context helpers map |
addContextHelper | (name: string, helper: ContextHelperFn) => void | Register or update a context helper by name |
removeContextHelper | (name: string) => void | Remove a context helper by name |
useTamboCurrentMessage
Returns the current message from the TamboMessageProvider context. Use this inside components rendered via the message pipeline to access the message that triggered the component.
import { useTamboCurrentMessage } from "@tambo-ai/react";
function ComponentWithMessage() {
const message = useTamboCurrentMessage();
return (
<div>
<p>Message ID: {message.id}</p>
<p>Role: {message.role}</p>
</div>
);
}Returns: TamboThreadMessage — the current message.
Throws if used outside a TamboMessageProvider (i.e., outside the message rendering pipeline).
useTamboCurrentComponent
Returns metadata about the current component from the message context. Works for both AI-generated components and interactable components wrapped with withTamboInteractable.
import { useTamboCurrentComponent } from "@tambo-ai/react";
function ComponentInfo() {
const component = useTamboCurrentComponent();
if (!component) return null;
return (
<div>
<p>Component: {component.componentName}</p>
{component.interactableId && <p>ID: {component.interactableId}</p>}
</div>
);
}Return Values
Returns TamboCurrentComponent | null (null if used outside TamboMessageProvider).
| Property | Type | Description |
|---|---|---|
componentName | string | undefined | Component name from the message or interactable metadata |
props | Record<string, unknown> | Component props from the message content block |
interactableId | string | undefined | Interactable ID (only for withTamboInteractable components) |
description | string | undefined | Description (only for withTamboInteractable components) |
threadId | string | undefined | Thread ID (not available on messages directly, always undefined) |
useComponentContent
Returns metadata about the current component instance from the ComponentContentProvider context. This is a lower-level hook — useTamboStreamStatus uses it internally, and useTamboComponentState uses a non-throwing variant of the same context.
Most applications should use useTamboCurrentComponent instead. Use this hook when you need the component's instance ID, thread ID, or message ID directly.
import { useComponentContent } from "@tambo-ai/react";
function DebugOverlay() {
const { componentId, threadId, messageId, componentName } =
useComponentContent();
return (
<pre>
{componentName} ({componentId}) in thread {threadId}
</pre>
);
}Return Values
| Value | Type | Description |
|---|---|---|
componentId | string | Unique instance ID for the component |
threadId | string | Thread the component belongs to |
messageId | string | Message the component belongs to |
componentName | string | Registered component name |
Throws if used outside a component rendered via ComponentRenderer.
useIsTamboTokenUpdating
Returns whether a session token exchange is in progress. Useful for showing loading indicators or disabling UI during authentication token refresh.
import { useIsTamboTokenUpdating } from "@tambo-ai/react";
function AuthGuard({ children }: { children: React.ReactNode }) {
const isUpdating = useIsTamboTokenUpdating();
if (isUpdating) return <div>Authenticating...</div>;
return <>{children}</>;
}Returns: boolean — true while the session token is being refreshed.
Throws if used outside a TamboClientProvider.
Re-exported Hooks
The following hooks are re-exported from the base SDK for convenience:
useTamboClient
Access the Tambo API client directly.
import { useTamboClient } from "@tambo-ai/react";
function ApiExample() {
const client = useTamboClient();
const fetchData = async () => {
const threads = await client.beta.threads.list();
// ...
};
}useMessageImages
Manage staged images for message input.
import { useMessageImages } from "@tambo-ai/react";
const { images, addImage, addImages, removeImage, clearImages } =
useMessageImages();useTamboVoice
Record and transcribe speech.
import { useTamboVoice } from "@tambo-ai/react";
const {
startRecording,
stopRecording,
isRecording,
isTranscribing,
transcript,
transcriptionError,
mediaAccessError,
} = useTamboVoice();