@cyborg-sdk/react
v0.2.26
Published
React SDK for chat integration
Downloads
93
Maintainers
Readme
@cyborg-sdk/react
A React SDK that provides context-aware AI chat capabilities for your applications. Simply wrap your app with the provider and the chat interface appears automatically!
Features
- 🚀 Zero Configuration - Just add the provider and you're done
- 🎯 Context-Aware - AI understands your app's pages and user context
- 🔄 Automatic Interface - Chat UI renders automatically, no extra components needed
- 🪝 Hook-Based API - Update context from anywhere in your app
- 🛠️ Tool/Function Calling - Let the AI execute custom functions to fetch data and perform actions
- 💪 TypeScript First - Full type safety out of the box
- 📦 Lightweight - Minimal bundle size impact
Installation
npm install @cyborg-sdk/reactQuick Start
1. Wrap Your App with CyborgProvider
The CyborgProvider automatically renders the chat interface - no need to add any other components!
// App.tsx
import { CyborgProvider } from '@cyborg-sdk/react';
function App() {
return (
<CyborgProvider config={{ publishableKey: "cpk_live_xxx" }}>
{/* Your app content */}
{/* The chat interface is automatically rendered! */}
<Router>
<Routes>
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/products" element={<Products />} />
</Routes>
</Router>
</CyborgProvider>
);
}That's it! The chat interface will now appear in your app.
2. Add Context from Any Component (Coming Soon)
Use the useCyborg hook to provide context about your pages and users. The chat assistant will automatically understand what page the user is on and their session data to provide relevant help.
// Dashboard.tsx
import { useCyborg } from '@cyborg-sdk/react';
function Dashboard() {
// Update the chat context with page and session data
useCyborg({
pageData: {
currentPage: '/dashboard',
description: 'User dashboard showing account overview, recent activity, and quick actions.',
widgets: ['account-summary', 'recent-orders', 'quick-actions'],
},
sessionData: {
userId: 'user123',
subscriptionTier: 'premium',
recentActivity: ['viewed-product-X'],
}
});
return (
<div>
{/* Your dashboard UI */}
</div>
);
}Architecture
The SDK uses a React Context-based architecture that allows you to update the chat's context from anywhere in your application:
- Automatic Rendering:
CyborgProviderautomatically renders the chat interface - Context Updates: Use
useCyborg()hook to update both page and session data - Smart AI Responses: Context data is automatically added to the AI's system prompt
- No Prop Drilling: Just call the hook where you need it
┌─────────────────────────────────────────┐
│ Your App (App.tsx) │
│ ┌───────────────────────────────────┐ │
│ │ CyborgProvider │ │
│ │ (automatically renders chat UI) │ │
│ │ │ │
│ │ ┌─────────────┐ ┌────────────┐ │ │
│ │ │ Dashboard │ │ Products │ │ │
│ │ │ │ │ │ │ │
│ │ │ useChatCtx()│ │useChatCtx()│ │ │
│ │ │ │ │ │ │ │
│ │ └─────────────┘ └────────────┘ │ │
│ └───────────────────────────────────┘ │
└─────────────────────────────────────────┘API Reference
CyborgProvider
The main provider component that wraps your application and automatically renders the chat interface.
<CyborgProvider config={config}>
{children}
</CyborgProvider>Props:
config: Configuration objectpublishableKey(required): Your publishable key (cpk_*)apiUrl(optional): Custom API endpointdebug(optional): Enable debug mode
useChat
Hook to interact with the chat functionality directly.
const { messages, isLoading, error, sendMessage, clearMessages } = useChat();Returns:
messages: Array of chat messagesisLoading: Boolean indicating if a message is being senterror: Error object if something went wrongsendMessage(content: string): Function to send a messageclearMessages(): Function to clear all messages
useCyborg
Hook to provide page and session context to the chat assistant, and to register custom tools for the AI to use. Call this in any component to update the context or register tools.
useCyborg({
pageData: {
currentPage: '/products',
description: 'Product catalog page',
// Any page-specific data
},
sessionData: {
userId: 'user123',
// Any user/session-specific data
},
tools: [
// Optional: Register tools for the AI to use
{
name: 'search_products',
description: 'Search for products in the catalog',
parameters: {
type: 'object',
properties: {
query: { type: 'string', description: 'Search query' }
},
required: ['query']
},
handler: async (params) => {
const response = await fetch(`/api/products?q=${params.query}`);
return response.json();
}
}
]
});Parameters:
pageData(optional): Object containing page-specific contextcurrentPage: Current route/pathdescription: Description of what the page does- ...any other page-specific fields
sessionData(optional): Object containing user/session-specific contextuserId: Current user ID- ...any other session-specific fields
tools(optional): Array of tool definitions for the AI to use
Returns:
setPageData(data): Function to update page context dynamicallysetSessionData(data): Function to update session context dynamicallyregisterTool(tool): Function to register a tool dynamically
Development
Running Storybook
We use Storybook for component development and documentation:
npm run storybookVisit http://localhost:6006/ to see interactive examples and documentation.
Building
npm run buildType Checking
npm run type-checkLinting
npm run lintExamples
Basic Integration
import { CyborgProvider } from '@cyborg-sdk/react';
function App() {
return (
<CyborgProvider config={{ publishableKey: "cpk_live_xxx" }}>
<YourApp />
</CyborgProvider>
);
}Multi-Page Application
// App.tsx
import { CyborgProvider } from '@cyborg-sdk/react';
function App() {
return (
<CyborgProvider config={{ publishableKey: "cpk_live_xxx" }}>
<Router>
<Routes>
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/products" element={<Products />} />
<Route path="/checkout" element={<Checkout />} />
</Routes>
</Router>
</CyborgProvider>
);
}
// Dashboard.tsx
import { useCyborg } from '@cyborg-sdk/react';
function Dashboard() {
useCyborg({
pageData: {
currentPage: '/dashboard',
description: 'User dashboard with account overview and quick actions',
},
sessionData: {
userId: 'user123',
subscriptionTier: 'premium',
}
});
return <div>{/* Dashboard content */}</div>;
}
// Products.tsx
import { useCyborg } from '@cyborg-sdk/react';
function Products() {
useCyborg({
pageData: {
currentPage: '/products',
description: 'Product catalog with filtering and search capabilities',
}
// sessionData can be omitted if not needed on this page
});
return <div>{/* Products content */}</div>;
}Using the Chat Hook Directly
import { useChat } from '@cyborg-sdk/react';
function CustomChatComponent() {
const { messages, sendMessage, isLoading, error } = useChat();
const handleSend = async () => {
await sendMessage('Hello!');
};
return (
<div>
{messages.map(msg => (
<div key={msg.id}>{msg.content}</div>
))}
{isLoading && <div>Loading...</div>}
{error && <div>Error: {error.message}</div>}
<button onClick={handleSend}>Send</button>
</div>
);
}TypeScript
This SDK is written in TypeScript and provides full type definitions.
import type { CyborgConfig, Message, CyborgChatContextValue, ToolDefinition } from '@cyborg-sdk/react';
const config: CyborgConfig = {
publishableKey: 'cpk_live_xxx',
debug: true,
};
// Type-safe tool definition
const searchTool: ToolDefinition = {
name: 'search_products',
description: 'Search for products',
parameters: {
type: 'object',
properties: {
query: { type: 'string' }
},
required: ['query']
},
handler: async (params: { query: string }) => {
// Fully typed!
return { results: [] };
}
};Tool Support (Function Calling)
The SDK supports tool/function calling, allowing the AI to execute custom functions you provide. This enables the AI to fetch data, perform actions, and interact with your application.
Basic Usage
import { CyborgProvider, useCyborg } from '@cyborg-sdk/react';
function OrdersPage() {
useCyborg({
pageData: { currentPage: '/orders' },
tools: [
{
name: 'get_user_orders',
description: 'Fetch recent orders for the current user',
parameters: {
type: 'object',
properties: {
limit: { type: 'number', description: 'Number of orders to fetch' },
status: {
type: 'string',
enum: ['pending', 'shipped', 'delivered'],
description: 'Filter by order status'
}
},
required: ['limit']
},
handler: async (params) => {
const response = await fetch(
`/api/orders?limit=${params.limit}&status=${params.status}`
);
return response.json();
}
}
]
});
return <div>Orders Page</div>;
}Dynamic Tool Registration
Tools can also be registered dynamically after mount:
function DynamicPage() {
const { registerTool } = useCyborg();
useEffect(() => {
registerTool({
name: 'dynamic_tool',
description: 'A dynamically registered tool',
parameters: { type: 'object', properties: {} },
handler: async () => ({ status: 'ok' })
});
}, [registerTool]);
return <div>Dynamic Tools Page</div>;
}Tool Definition
A tool definition consists of:
name: Unique identifier for the tool (use snake_case)description: Clear description of what the tool does (helps the AI decide when to use it)parameters: JSON Schema object describing the parametershandler: Async function that executes the tool and returns resultstimeout(optional): Maximum execution time in ms (default: 30000)
Parameter Schema
Use JSON Schema to define tool parameters. The AI will validate parameters before calling your handler:
parameters: {
type: 'object',
properties: {
query: {
type: 'string',
description: 'Search query'
},
limit: {
type: 'number',
description: 'Max results (1-100)'
},
category: {
type: 'string',
enum: ['electronics', 'clothing', 'books'],
description: 'Filter by category'
}
},
required: ['query'] // Make query required
}Supported types: string, number, boolean, object, array
Error Handling
Tool errors are automatically caught and returned to the AI, which can respond appropriately:
handler: async (params) => {
// Authentication check
if (!currentUser) {
throw new Error('Authentication required');
}
// API call with error handling
const response = await fetch('/api/data');
if (!response.ok) {
throw new Error(`API error: ${response.status}`);
}
return response.json();
}Best practices:
- Throw descriptive Error objects with clear messages
- The AI will see the error message and can explain it to the user
- Don't crash - errors are part of the normal flow
Authentication & Authorization
Always handle auth/permissions in your tool handlers:
handler: async (params) => {
// Check authentication
if (!currentUser?.isAuthenticated) {
throw new Error('Please sign in to view orders');
}
// Check permissions
if (!currentUser.hasPermission('read:orders')) {
throw new Error('You do not have permission to view orders');
}
// Proceed with operation
return await fetchOrders(currentUser.id, params);
}Timeouts & Size Limits
Configure timeouts and result size limits to protect your app:
// Per-tool timeout
const slowTool: ToolDefinition = {
name: 'slow_operation',
description: 'A slow operation',
parameters: { type: 'object', properties: {} },
handler: async () => {
// This operation has 60 seconds instead of default 30s
await longRunningOperation();
return { status: 'done' };
},
timeout: 60000 // 60 seconds
};
// Global result size limit
<CyborgProvider
config={{
publishableKey: 'cpk_live_xxx',
maxToolResultSizeKb: 50 // Limit tool results to 50KB (default: 100KB)
}}
>
<App />
</CyborgProvider>Defaults:
- Timeout: 30 seconds (30000ms)
- Max result size: 100KB
Best Practices
- Keep handlers simple: One clear purpose per tool
- Add timeouts: Prevent hanging UX with reasonable timeouts for slow operations
- Validate auth: Always check user permissions in handlers
- Return structured data: Use objects/arrays, not plain strings
- Handle errors gracefully: Throw descriptive Error objects
- Use TypeScript: Get type safety for parameters and return values
- Clear descriptions: Help the AI understand when to use each tool
- Test thoroughly: Tools run in the user's browser with their permissions
Tool Execution Flow
- User sends message → "What are my recent orders?"
- AI analyzes → Decides to use
get_user_orderstool - Tool call emitted → SDK receives tool call from backend
- Handler executes → Your
handlerfunction runs in the browser - Results sent back → Tool results sent to AI
- AI responds → "You have 3 recent orders: Order #123 (shipped), ..."
Performance notes:
- Multiple tools execute in parallel using
Promise.all() - Tool metadata is sent with each message (~1KB per tool)
- Handlers run in the browser, not on the backend
Debug Mode
Enable debug mode to inspect the tool registry:
<CyborgProvider config={{ publishableKey: 'cpk_live_xxx', debug: true }}>
<App />
</CyborgProvider>Then in browser console:
window.__CYBORG_SNAPSHOT__()
// Shows: { toolRegistry: { ... }, pageDataRegistry: { ... }, ... }You can inspect:
- Which tools are registered
- Tool definitions (name, description, parameters)
- Page and session context data
Lifecycle
- Tools registered via
options.toolsare registered on component mount - Tools registered via
registerTool()can be added anytime - ALL tools (declarative and imperative) are automatically cleaned up on unmount
- No manual cleanup needed - prevents memory leaks automatically
- Tools from multiple components are merged automatically
Example: E-commerce Store
import { useCyborg } from '@cyborg-sdk/react';
function ProductPage({ productId }: { productId: string }) {
useCyborg({
pageData: {
currentPage: `/products/${productId}`,
productId,
},
tools: [
{
name: 'check_inventory',
description: 'Check if a product is in stock',
parameters: {
type: 'object',
properties: {
productId: { type: 'string' }
},
required: ['productId']
},
handler: async ({ productId }) => {
const res = await fetch(`/api/inventory/${productId}`);
return res.json(); // { inStock: true, quantity: 5 }
}
},
{
name: 'add_to_cart',
description: 'Add a product to the shopping cart',
parameters: {
type: 'object',
properties: {
productId: { type: 'string' },
quantity: { type: 'number' }
},
required: ['productId', 'quantity']
},
handler: async ({ productId, quantity }) => {
const res = await fetch('/api/cart', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ productId, quantity })
});
return res.json(); // { success: true, cartTotal: 3 }
}
},
{
name: 'get_similar_products',
description: 'Find similar products based on a product ID',
parameters: {
type: 'object',
properties: {
productId: { type: 'string' },
limit: { type: 'number' }
},
required: ['productId']
},
handler: async ({ productId, limit = 5 }) => {
const res = await fetch(
`/api/products/similar?id=${productId}&limit=${limit}`
);
return res.json(); // { products: [...] }
}
}
]
});
return <div>Product Page Content</div>;
}Now users can ask:
- "Is this product in stock?"
- "Add 2 of these to my cart"
- "Show me similar products"
And the AI will use your tools to provide accurate, real-time answers!
Styling
The SDK uses Tailwind CSS v4 for styling, with all utility classes prefixed with chat: to prevent conflicts with your application's styles.
Class Prefix
All Tailwind classes used by the SDK have the chat: prefix (note the colon):
chat:bg-primary-500instead ofbg-primary-500chat:p-4instead ofp-4chat:rounded-lginstead ofrounded-lg
This means the chat interface won't interfere with your own Tailwind classes or other CSS frameworks.
Important: Tailwind v4 uses a colon (:) separator for prefixes, not a hyphen (-). This is different from Tailwind v3.
Customization
If you need to customize the chat interface styling, you can:
Override with CSS: Target the chat components using standard CSS:
/* Override chat button color */ .chat\:bg-primary-500 { background-color: your-custom-color !important; }Note: You need to escape the colon with a backslash in CSS selectors.
Use CSS Custom Properties: The SDK respects standard CSS custom properties for theming (future enhancement).
Fork and Modify: For extensive customization, you can fork the SDK and modify the Tailwind configuration in
src/styles.css.
Bundle Size
The SDK uses Tailwind CSS v4 which automatically optimizes your CSS, ensuring only the styles actually used by the chat interface are included in the bundle (~10-15kb gzipped).
License
MIT
Contributing
Contributions are welcome! Please open an issue or submit a pull request.
