Overview
The useTamboThreadInput() hook provides shared input state for thread messages. All components using this hook share the same input state, enabling features like suggestions to update the input field directly.
This hook manages text input, image attachments, and message submission with optimistic updates and error handling.
Import
import { useTamboThreadInput } from '@tambo-ai/react';
Usage
import { useTamboThreadInput } from '@tambo-ai/react';
function ChatInput() {
const { value, setValue, submit, isPending } = useTamboThreadInput();
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
await submit();
};
return (
<form onSubmit={handleSubmit}>
<input
value={value}
onChange={(e) => setValue(e.target.value)}
disabled={isPending}
placeholder="Type a message..."
/>
<button type="submit" disabled={isPending}>
{isPending ? 'Sending...' : 'Send'}
</button>
</form>
);
}
Return Value
Current value of the input field.
setValue
React.Dispatch<React.SetStateAction<string>>
Function to update the input value. Accepts a new string value or a function that receives the previous value.setValue('Hello');
setValue(prev => prev + ' World');
Submission
submit
(options?: SubmitOptions) => Promise<{ threadId: string | undefined }>
Submit the current input value as a message. Returns a promise that resolves with the thread ID.Throws an error if:
- Authentication is not ready (
isIdentified is false)
- Input is empty and no images are attached
The input is optimistically cleared on submission so users can start typing the next message immediately.
Whether a message submission is currently in progress.
Whether the last submission completed successfully.
Whether the last submission resulted in an error.
Error object from the last failed submission, if any.
data
{ threadId: string | undefined } | undefined
Result data from the last successful submission.
Image Attachments
Array of currently staged images. Each image has:
id: string - Unique identifier
name: string - File name
type: string - MIME type
dataUrl: string - Data URL for display
addImage
(file: File) => Promise<void>
Add a single image file to the staged images. The file is converted to a data URL for preview and transmission.
addImages
(files: File[]) => Promise<void>
Add multiple image files to the staged images.
Remove an image from the staged images by its ID.
Thread State
Current thread ID being used for input (from stream state). May be undefined for new threads.
Whether the input should be disabled. True when submission is pending or authentication is not ready.
Additional React Query State
status
'idle' | 'pending' | 'success' | 'error'
Current status of the submission mutation.
Number of consecutive failures for the current submission.
Error that caused the current failure state.
Reset the mutation state to idle.
Type Definitions
TamboThreadInputContextProps
interface TamboThreadInputContextProps {
// Input state
value: string;
setValue: React.Dispatch<React.SetStateAction<string>>;
// Submission
submit: (options?: SubmitOptions) => Promise<{ threadId: string | undefined }>;
isPending: boolean;
isSuccess: boolean;
isError: boolean;
error: Error | null;
data: { threadId: string | undefined } | undefined;
status: 'idle' | 'pending' | 'success' | 'error';
// Images
images: StagedImage[];
addImage: (file: File) => Promise<void>;
addImages: (files: File[]) => Promise<void>;
removeImage: (id: string) => void;
clearImages: () => void;
// Thread state
threadId: string | undefined;
isDisabled: boolean;
// React Query state
failureCount: number;
failureReason: Error | null;
reset: () => void;
}
SubmitOptions
interface SubmitOptions {
/**
* Enable debug logging for the stream
*/
debug?: boolean;
/**
* How the model should use tools. Defaults to "auto".
* - "auto": Model decides whether to use tools
* - "required": Model must use at least one tool
* - "none": Model cannot use tools
* - { name: "toolName" }: Model must use the specified tool
*/
toolChoice?: ToolChoice;
}
StagedImage
interface StagedImage {
id: string;
name: string;
type: string;
dataUrl: string;
}
Examples
With Image Upload
function ChatInputWithImages() {
const {
value,
setValue,
submit,
isPending,
images,
addImage,
removeImage,
} = useTamboThreadInput();
const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const files = Array.from(e.target.files || []);
files.forEach(file => addImage(file));
};
return (
<form onSubmit={(e) => { e.preventDefault(); submit(); }}>
<div className="image-preview">
{images.map(img => (
<div key={img.id}>
<img src={img.dataUrl} alt={img.name} />
<button onClick={() => removeImage(img.id)}>×</button>
</div>
))}
</div>
<input
type="file"
accept="image/*"
multiple
onChange={handleFileChange}
/>
<textarea
value={value}
onChange={(e) => setValue(e.target.value)}
disabled={isPending}
placeholder="Type a message..."
/>
<button type="submit" disabled={isPending}>
Send
</button>
</form>
);
}
function ToolForcedInput() {
const { value, setValue, submit, isPending } = useTamboThreadInput();
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
// Force the AI to use the search tool
await submit({
toolChoice: { name: 'search' }
});
};
return (
<form onSubmit={handleSubmit}>
<input
value={value}
onChange={(e) => setValue(e.target.value)}
placeholder="Search query..."
disabled={isPending}
/>
<button type="submit" disabled={isPending}>
Search
</button>
</form>
);
}
With Error Handling
function ChatInputWithErrors() {
const {
value,
setValue,
submit,
isPending,
isError,
error,
reset,
} = useTamboThreadInput();
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
try {
await submit();
} catch (err) {
// Error is already captured in `error` state
console.error('Failed to send message:', err);
}
};
return (
<div>
{isError && (
<div className="error">
<span>Error: {error?.message}</span>
<button onClick={reset}>Dismiss</button>
</div>
)}
<form onSubmit={handleSubmit}>
<input
value={value}
onChange={(e) => setValue(e.target.value)}
disabled={isPending}
/>
<button type="submit" disabled={isPending}>
Send
</button>
</form>
</div>
);
}
Keyboard Shortcuts
function ChatInputWithShortcuts() {
const { value, setValue, submit, isPending } = useTamboThreadInput();
const handleKeyDown = (e: React.KeyboardEvent) => {
// Submit on Cmd+Enter or Ctrl+Enter
if ((e.metaKey || e.ctrlKey) && e.key === 'Enter') {
e.preventDefault();
submit();
}
};
return (
<textarea
value={value}
onChange={(e) => setValue(e.target.value)}
onKeyDown={handleKeyDown}
disabled={isPending}
placeholder="Type a message (Cmd+Enter to send)..."
/>
);
}