React hooks and components for Platform AI SDK
npm install @dclbs/reactbash
npm install @dclbs/react @dclbs/core
`
Hooks
$3
Complete chat functionality with streaming support:
`tsx
import { useAgentChat } from "@dclbs/react";
function ChatComponent() {
const { messages, sendMessage, isStreaming } = useAgentChat({
assistantId: "your-assistant-id",
apiUrl: "https://api.caava.ai",
apiKey: "your-api-key",
onMessage: (message) => console.log("New message:", message),
onError: (error) => console.error("Chat error:", error),
});
return (
{messages.map((msg, i) => (
{msg.content}
))}
);
}
`
$3
Advanced streaming with branching and state management:
`tsx
import { useOptimizedStream } from "@dclbs/react";
function StreamingComponent() {
const { messages, sendMessage, isStreaming, branchFromMessage } =
useOptimizedStream({
assistantId: "your-assistant-id",
enableBranching: true,
maxRetries: 3,
});
return (
{messages.map((msg, i) => (
{msg.content}
))}
);
}
`
$3
Comprehensive file upload functionality with drag & drop, paste support, and validation:
`tsx
import { useFileUpload } from "@dclbs/react";
function FileUploadComponent() {
const {
files,
isDragOver,
isUploading,
fileInputRef,
handleFileSelect,
handleDragOver,
handleDragLeave,
handleDrop,
handlePaste,
removeFile,
clearFiles,
openFilePicker,
} = useFileUpload({
maxFileSize: 10, // 10MB
supportedFileTypes: ["image/jpeg", "image/png", "application/pdf"],
maxFiles: 5,
onFileUpload: (files) => {
console.log("Files uploaded:", files);
},
onError: (error) => {
console.error("Upload error:", error);
},
});
return (
onDragOver={handleDragOver}
onDragLeave={handleDragLeave}
onDrop={handleDrop}
onPaste={handlePaste}
style={{
border: isDragOver ? "2px dashed #3b82f6" : "2px dashed #e5e7eb",
borderRadius: "8px",
padding: "20px",
textAlign: "center",
}}
>
{/ Hidden file input /}
ref={fileInputRef}
type="file"
multiple
accept={supportedFileTypes.join(",")}
style={{ display: "none" }}
onChange={(e) => handleFileSelect(e.target.files)}
/>
{/ Upload button /}
{/ File previews /}
{files.length > 0 && (
{files.map((fileItem, index) => (
key={index}
style={{
display: "flex",
alignItems: "center",
gap: "8px",
marginBottom: "8px",
}}
>
{/ Image preview /}
{fileItem.preview && (
src={fileItem.preview}
alt={fileItem.file.name}
style={{ width: "40px", height: "40px", objectFit: "cover" }}
/>
)}
{/ File info /}
{fileItem.file.name}
{(fileItem.file.size / 1024 / 1024).toFixed(2)} MB
{fileItem.status === "error" && (
{fileItem.error}
)}
{/ Remove button /}
onClick={() => removeFile(index)}
style={{ marginLeft: "auto" }}
>
×
))}
Drop files here, click to upload, or paste from clipboard
#### useFileUpload Options
`typescript
interface UseFileUploadOptions {
maxFileSize?: number; // Maximum file size in MB (default: 10)
supportedFileTypes?: string[]; // Array of MIME types (default: images + PDF)
maxFiles?: number; // Maximum number of files (default: 10)
onFileUpload?: (files: File[]) => void; // Callback when files are ready
onError?: (error: string) => void; // Error callback
}
`
#### useFileUpload Return
`typescript
interface UseFileUploadReturn {
files: UploadingFile[]; // Current files with status and previews
isDragOver: boolean; // True when dragging files over component
isUploading: boolean; // True when processing files
fileInputRef: React.RefObject; // Ref for hidden file input
// Actions
handleFileSelect: (files: FileList | null) => void; // Handle file input change
handleDragOver: (e: React.DragEvent) => void; // Handle drag over
handleDragLeave: (e: React.DragEvent) => void; // Handle drag leave
handleDrop: (e: React.DragEvent) => void; // Handle file drop
handlePaste: (e: React.ClipboardEvent) => void; // Handle clipboard paste
removeFile: (index: number) => void; // Remove file by index
clearFiles: () => void; // Clear all files
openFilePicker: () => void; // Programmatically open file picker
}
`
#### File Upload Features
- Drag & Drop: Drag files directly onto any element with drag handlers
- File Picker: Click button to open native file selection dialog
- Clipboard Paste: Paste images directly from clipboard (Ctrl+V)
- File Validation: Automatic validation of file size and type
- Image Previews: Automatic base64 preview generation for images
- Error Handling: Comprehensive error messages for validation failures
- Multiple Files: Support for multiple file selection and management
- Type Safety: Full TypeScript support with detailed type definitions
Components
$3
Ready-to-use chat component:
`tsx
import { AgentChat } from "@dclbs/react";
function App() {
return (
assistantId="your-assistant-id"
apiKey="your-api-key"
title="Customer Support"
enableFileUpload={true}
maxFileSize={10}
onMessage={(message) => console.log(message)}
onFileUpload={(files) => console.log("Files:", files)}
/>
);
}
`
Providers
$3
Context provider for managing streaming state:
`tsx
import { OptimizedStreamProvider } from "@dclbs/react";
function App() {
return (
);
}
`
Complete File Upload Example
Here's a complete example showing how to integrate file upload with chat:
`tsx
import React, { useState } from "react";
import { useAgentChat, useFileUpload } from "@dclbs/react";
function ChatWithFileUpload() {
const [inputValue, setInputValue] = useState("");
const { messages, sendMessage, isStreaming } = useAgentChat({
assistantId: "your-assistant-id",
apiKey: "your-api-key",
});
const {
files,
isDragOver,
handleDragOver,
handleDragLeave,
handleDrop,
handlePaste,
removeFile,
clearFiles,
fileInputRef,
openFilePicker,
handleFileSelect,
} = useFileUpload({
maxFileSize: 10,
supportedFileTypes: ["image/jpeg", "image/png", "application/pdf"],
onFileUpload: (uploadedFiles) => {
console.log("Files ready for sending:", uploadedFiles);
},
});
const handleSendMessage = async () => {
if (!inputValue.trim() && files.length === 0) return;
// Prepare message content with text and files
const content = [];
if (inputValue.trim()) {
content.push({
type: "text",
text: inputValue.trim(),
});
}
files.forEach((fileItem) => {
if (fileItem.status === "completed") {
if (fileItem.file.type.startsWith("image/") && fileItem.preview) {
content.push({
type: "image_url",
image_url: { url: fileItem.preview },
});
} else if (fileItem.file.type === "application/pdf") {
content.push({
type: "media",
media: {
type: "document",
name: fileItem.file.name,
size: fileItem.file.size,
mime_type: fileItem.file.type,
},
});
}
}
});
if (content.length > 0) {
await sendMessage(content);
setInputValue("");
clearFiles();
}
};
return (
style={{
width: "400px",
height: "500px",
border: "1px solid #e5e7eb",
borderRadius: "8px",
display: "flex",
flexDirection: "column",
}}
onDragOver={handleDragOver}
onDragLeave={handleDragLeave}
onDrop={handleDrop}
>
{/ Messages /}
{messages.map((msg, i) => (
{msg.role}: {msg.content}
))}
{isStreaming && Assistant is typing...}
{/ File Previews /}
{files.length > 0 && (
style={{
padding: "12px",
borderTop: "1px solid #e5e7eb",
backgroundColor: "#f9fafb",
}}
>
{files.map((fileItem, index) => (
key={index}
style={{
display: "flex",
alignItems: "center",
gap: "8px",
marginBottom: "8px",
}}
>
{fileItem.preview && (
src={fileItem.preview}
alt={fileItem.file.name}
style={{ width: "32px", height: "32px", objectFit: "cover" }}
/>
)}
{fileItem.file.name}
Drop your files here
TypeScript Support
All hooks and components are fully typed:
`typescript
import type {
UseOptimizedStreamOptions,
UseOptimizedStreamReturn,
UseFileUploadOptions,
UseFileUploadReturn,
UploadingFile,
} from "@dclbs/react";
`
Dependencies
- @dclbs/core - Core types and utilities
- react >= 18.0.0
- react-dom` >= 18.0.0