threads

Manages conversations, suggestions, voice input, and image attachments.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "threads" with this command: npx skills add tambo-ai/tambo/tambo-ai-tambo-threads

Threads and Input

Manages conversations, suggestions, voice input, and image attachments.

Quick Start

import { useTambo, useTamboThreadInput } from "@tambo-ai/react";

const { thread, messages, isIdle } = useTambo(); const { value, setValue, submit } = useTamboThreadInput();

await submit(); // sends current input value

Thread Management

Access and manage the current thread using useTambo() and useTamboThreadInput() :

import { useTambo, useTamboThreadInput, ComponentRenderer, } from "@tambo-ai/react";

function Chat() { const { thread, // Current thread state messages, // Messages with computed properties isIdle, // True when not generating isStreaming, // True when streaming response isWaiting, // True when waiting for server currentThreadId, // Active thread ID switchThread, // Switch to different thread startNewThread, // Create new thread, returns ID cancelRun, // Cancel active generation } = useTambo();

const { value, // Current input value setValue, // Update input submit, // Send message isPending, // Submission in progress images, // Staged image files addImage, // Add single image removeImage, // Remove image by ID } = useTamboThreadInput();

const handleSend = async () => { await submit(); };

return ( <div> {messages.map((msg) => ( <div key={msg.id}> {msg.content.map((block) => { switch (block.type) { case "text": return <p key={${msg.id}:text}>{block.text}</p>; case "component": return ( <ComponentRenderer key={block.id} content={block} threadId={currentThreadId} messageId={msg.id} /> ); case "tool_use": return ( <div key={block.id}> {block.statusMessage ?? Running ${block.name}...} </div> ); default: return null; } })} </div> ))} <input value={value} onChange={(e) => setValue(e.target.value)} /> <button onClick={handleSend} disabled={!isIdle || isPending}> Send </button> </div> ); }

Streaming State

Property Type Description

isIdle

boolean

Not generating

isWaiting

boolean

Waiting for server response

isStreaming

boolean

Actively streaming response

The streamingState object provides additional detail:

const { streamingState } = useTambo(); // streamingState.status: "idle" | "waiting" | "streaming" // streamingState.runId: current run ID // streamingState.error: { message, code } if error occurred

Content Block Types

Messages contain an array of content blocks. Handle each type:

Type Description Key Fields

text

Plain text text

component

AI-generated component id , name , props

tool_use

Tool invocation id , name , input

tool_result

Tool response toolUseId , content

resource

MCP resource uri , name , text

Submit Options

const { submit } = useTamboThreadInput();

await submit({ toolChoice: "auto", // "auto" | "required" | "none" | { name: "toolName" } debug: true, // Enable debug logging for the stream });

Fetching a Thread by ID

To fetch a specific thread (e.g., for a detail view), use useTamboThread(threadId) :

import { useTamboThread } from "@tambo-ai/react";

function ThreadView({ threadId }: { threadId: string }) { const { data: thread, isLoading, isError } = useTamboThread(threadId);

if (isLoading) return <Skeleton />; if (isError) return <div>Failed to load thread</div>;

return <div>{thread.name}</div>; }

This is a React Query hook - use it for read-only thread fetching, not for the active conversation.

Thread List

Manage multiple conversations:

import { useTambo, useTamboThreadList } from "@tambo-ai/react";

function ThreadSidebar() { const { data, isLoading } = useTamboThreadList(); const { currentThreadId, switchThread, startNewThread } = useTambo();

if (isLoading) return <Skeleton />;

return ( <div> <button onClick={() => startNewThread()}>New Thread</button> <ul> {data?.threads.map((t) => ( <li key={t.id}> <button onClick={() => switchThread(t.id)} className={currentThreadId === t.id ? "active" : ""} > {t.name || "Untitled"} </button> </li> ))} </ul> </div> ); }

Thread List Options

const { data } = useTamboThreadList({ userKey: "user_123", // Filter by user (defaults to provider's userKey) limit: 20, // Max results cursor: nextCursor, // Pagination cursor });

// data.threads: TamboThread[] // data.hasMore: boolean // data.nextCursor: string

Suggestions

AI-generated follow-up suggestions after each assistant message:

import { useTamboSuggestions } from "@tambo-ai/react";

function Suggestions() { const { suggestions, isLoading, accept, isAccepting } = useTamboSuggestions({ maxSuggestions: 3, // 1-10, default 3 autoGenerate: true, // Auto-generate after assistant message });

if (isLoading) return <Skeleton />;

return ( <div className="suggestions"> {suggestions.map((s) => ( <button key={s.id} onClick={() => accept({ suggestion: s })} disabled={isAccepting} > {s.title} </button> ))} </div> ); }

Auto-Submit Suggestion

// Accept and immediately submit as a message accept({ suggestion: s, shouldSubmit: true });

Manual Generation

const { generate, isGenerating } = useTamboSuggestions({ autoGenerate: false, // Disable auto-generation });

<button onClick={() => generate()} disabled={isGenerating}> Get suggestions </button>;

Voice Input

Speech-to-text transcription:

import { useTamboVoice } from "@tambo-ai/react";

function VoiceButton() { const { startRecording, stopRecording, isRecording, isTranscribing, transcript, transcriptionError, mediaAccessError, } = useTamboVoice();

return ( <div> <button onClick={isRecording ? stopRecording : startRecording}> {isRecording ? "Stop" : "Record"} </button> {isTranscribing && <span>Transcribing...</span>} {transcript && <p>{transcript}</p>} {transcriptionError && <p className="error">{transcriptionError}</p>} </div> ); }

Voice Hook Returns

Property Type Description

startRecording

() => void

Start recording, reset transcript

stopRecording

() => void

Stop and start transcription

isRecording

boolean

Currently recording

isTranscribing

boolean

Processing audio

transcript

string | null

Transcribed text

transcriptionError

string | null

Transcription error

mediaAccessError

string | null

Mic access error

Image Attachments

Images are managed via useTamboThreadInput() :

import { useTamboThreadInput } from "@tambo-ai/react";

function ImageInput() { const { images, addImage, addImages, removeImage, clearImages } = useTamboThreadInput();

const handleFiles = async (files: FileList) => { await addImages(Array.from(files)); };

return ( <div> <input type="file" accept="image/*" multiple onChange={(e) => handleFiles(e.target.files!)} /> {images.map((img) => ( <div key={img.id}> <img src={img.dataUrl} alt={img.name} /> <button onClick={() => removeImage(img.id)}>Remove</button> </div> ))} </div> ); }

StagedImage Properties

Property Type Description

id

string

Unique image ID

name

string

File name

dataUrl

string

Base64 data URL

file

File

Original File object

size

number

File size in bytes

type

string

MIME type

User Authentication

Enable per-user thread isolation:

import { TamboProvider } from "@tambo-ai/react";

function App() { return ( <TamboProvider apiKey={apiKey} userKey="user_123" // Simple user identifier > <Chat /> </TamboProvider> ); }

For OAuth-based auth, use userToken instead:

function App() { const userToken = useUserToken(); // From your auth provider

return ( <TamboProvider apiKey={apiKey} userToken={userToken}> <Chat /> </TamboProvider> ); }

Use userKey for simple user identification or userToken for OAuth JWT tokens. Don't use both.

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

generative-ui

No summary provided by upstream source.

Repository SourceNeeds Review
General

add-components-to-registry

No summary provided by upstream source.

Repository SourceNeeds Review
General

components

No summary provided by upstream source.

Repository SourceNeeds Review