npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@cyborg-sdk/react

v0.2.26

Published

React SDK for chat integration

Downloads

93

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/react

Quick 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:

  1. Automatic Rendering: CyborgProvider automatically renders the chat interface
  2. Context Updates: Use useCyborg() hook to update both page and session data
  3. Smart AI Responses: Context data is automatically added to the AI's system prompt
  4. 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 object
    • publishableKey (required): Your publishable key (cpk_*)
    • apiUrl (optional): Custom API endpoint
    • debug (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 messages
  • isLoading: Boolean indicating if a message is being sent
  • error: Error object if something went wrong
  • sendMessage(content: string): Function to send a message
  • clearMessages(): 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 context
    • currentPage: Current route/path
    • description: Description of what the page does
    • ...any other page-specific fields
  • sessionData (optional): Object containing user/session-specific context
    • userId: 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 dynamically
  • setSessionData(data): Function to update session context dynamically
  • registerTool(tool): Function to register a tool dynamically

Development

Running Storybook

We use Storybook for component development and documentation:

npm run storybook

Visit http://localhost:6006/ to see interactive examples and documentation.

Building

npm run build

Type Checking

npm run type-check

Linting

npm run lint

Examples

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 parameters
  • handler: Async function that executes the tool and returns results
  • timeout (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

  1. Keep handlers simple: One clear purpose per tool
  2. Add timeouts: Prevent hanging UX with reasonable timeouts for slow operations
  3. Validate auth: Always check user permissions in handlers
  4. Return structured data: Use objects/arrays, not plain strings
  5. Handle errors gracefully: Throw descriptive Error objects
  6. Use TypeScript: Get type safety for parameters and return values
  7. Clear descriptions: Help the AI understand when to use each tool
  8. Test thoroughly: Tools run in the user's browser with their permissions

Tool Execution Flow

  1. User sends message → "What are my recent orders?"
  2. AI analyzes → Decides to use get_user_orders tool
  3. Tool call emitted → SDK receives tool call from backend
  4. Handler executes → Your handler function runs in the browser
  5. Results sent back → Tool results sent to AI
  6. 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.tools are 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-500 instead of bg-primary-500
  • chat:p-4 instead of p-4
  • chat:rounded-lg instead of rounded-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:

  1. 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.

  2. Use CSS Custom Properties: The SDK respects standard CSS custom properties for theming (future enhancement).

  3. 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.