npm package: @rodgerai/ui
Description: React UI components for Rodger.ai agents
Author: Rodger.ai
Latest version: 2.0.4

# @rodger/ui

Beautiful, customizable React components for building AI chat interfaces powered by Rodger.ai agents.

## Features

- Pre-built Chat UI - Thread, messages, composer components
- useRodgerChat Hook - Simple integration with backend agents
- Widget System - Extensible tool result rendering
- Built on assistant-ui - Leverage the powerful assistant-ui primitives
- TypeScript-First - Full type safety and IntelliSense
- Tailwind Styled - Easy customization with Tailwind CSS
- Animated - Smooth animations with Motion

## Installation

```bash
npm install @rodger/ui @rodger/core
# or
pnpm add @rodger/ui @rodger/core
```

## Peer Dependencies

This package requires:

```json
{
  "react": "^18.0.0 || ^19.0.0",
  "react-dom": "^18.0.0 || ^19.0.0",
  "@assistant-ui/react": "^0.11.0",
  "@assistant-ui/react-ai-sdk": "^1.1.0"
}
```

These will be automatically installed if not present.

## Quick Start

### 1. Set Up Your Backend API

Create an API route that uses @rodger/core:

```typescript
// app/api/chat/route.ts
import { createAgent } from '@rodger/core';
import { streamText } from 'ai';

const agent = createAgent({
  name: 'Chat Agent',
  llm: { provider: 'openai', model: 'gpt-4o' },
  systemPrompt: 'You are a helpful assistant.'
});

export async function POST(req: Request) {
  const { messages, sessionId } = await req.json();
  const userMessage = messages[messages.length - 1].content;

  // Stream response from agent
  const response = await agent.chat(userMessage, { sessionId });

  return streamText({
    text: response.text
  });
}
```

### 2. Build Your Chat UI

```tsx
'use client';

import { RodgerProvider, Thread, useRodgerChat } from '@rodger/ui';

export default function ChatPage() {
  const { runtime, isLoadingHistory } = useRodgerChat({
    endpoint: '/api/chat',
    sessionId: 'user-session-123'
  });

  if (isLoadingHistory) {
    return <div>Loading conversation...</div>;
  }

  return (
    <RodgerProvider runtime={runtime}>
      <div className="h-screen">
        <Thread
          welcomeMessage={
            <div>
              <h1 className="text-4xl font-bold">Welcome!</h1>
              <p className="text-muted-foreground">How can I help you today?</p>
            </div>
          }
          suggestions={[
            { title: 'Weather', label: 'Get the weather', action: 'What is the weather?' },
            { title: 'Joke', label: 'Tell me a joke', action: 'Tell me a joke' }
          ]}
          placeholder="Ask me anything..."
        />
      </div>
    </RodgerProvider>
  );
}
```

That's it! You now have a fully functional chat interface.

## Core Components

### Thread

Main chat interface displaying messages and composer.

```tsx
import { Thread } from '@rodger/ui';

<Thread
  // Optional: Custom welcome message
  welcomeMessage={<CustomWelcome />}

  // Optional: Suggested prompts
  suggestions={[
    {
      title: 'Quick Start',
      label: 'Get started',
      action: 'Help me get started'
    }
  ]}

  // Optional: Custom placeholder
  placeholder="Send a message..."

  // Optional: Custom widgets for tool results
  widgets={{
    myTool: MyCustomWidget
  }}

  // Optional: Custom CSS classes
  className="custom-thread"
/>
```

**Props:**
- `welcomeMessage` (ReactNode) - Custom welcome screen content
- `suggestions` (Suggestion[]) - Suggestion chips for quick actions
- `placeholder` (string) - Input placeholder text
- `widgets` (Record<string, FC>) - Custom widgets for tool results
- `className` (string) - Additional CSS classes

### RodgerProvider

Context provider that wraps your chat UI.

```tsx
import { RodgerProvider } from '@rodger/ui';

<RodgerProvider runtime={runtime}>
  {/* Your chat UI components */}
  <Thread />
</RodgerProvider>
```

**Props:**
- `runtime` (AssistantRuntime) - Runtime from useRodgerChat
- `children` (ReactNode) - Child components

**Features:**
- Provides runtime context to all child components
- Built-in error boundary for runtime errors
- Handles runtime lifecycle automatically

## Hooks

### useRodgerChat

Primary hook for connecting to your backend agent.

```tsx
import { useRodgerChat } from '@rodger/ui';

const { runtime, isLoadingHistory } = useRodgerChat({
  // Required: API endpoint
  endpoint: '/api/chat',

  // Required: Session identifier
  sessionId: 'user-123',

  // Optional: Additional request body params
  body: {
    maxSteps: 10,
    temperature: 0.7
  },

  // Optional: Lifecycle callbacks
  onBeforeRun: (input) => {
    console.log('User sent:', input);
  },
  onAfterRun: (output) => {
    console.log('Agent replied:', output);
  }
});
```

**Options:**
- `endpoint` (string) - API route for chat
- `sessionId` (string) - Unique session ID for conversation history
- `body` (object, optional) - Additional parameters sent with each request
- `onBeforeRun` (function, optional) - Called before agent processes input
- `onAfterRun` (function, optional) - Called after agent responds

**Returns:**
- `runtime` (AssistantRuntime) - Runtime instance for RodgerProvider
- `isLoadingHistory` (boolean) - Loading state for message history

**Features:**
- Automatic message history loading from `{endpoint}/history`
- AssistantChatTransport configuration
- Lifecycle hooks for monitoring
- Session-based conversation management

### useChat

Low-level hook for direct assistant-ui integration.

```tsx
import { useChat } from '@rodger/ui';

const { messages, append, isLoading } = useChat({
  api: '/api/chat'
});
```

See [@assistant-ui/react documentation](https://assistant-ui.com) for full details.

## Custom Widgets

Widgets render tool results with custom UI components.

### Creating a Widget

```tsx
// components/WeatherWidget.tsx
import { makeAssistantToolUI } from '@assistant-ui/react';

export const WeatherWidget = makeAssistantToolUI({
  toolName: 'getWeather',
  render: ({ result }) => {
    const weather = result as { temp: number; condition: string };

    return (
      <div className="p-4 bg-blue-50 rounded-lg">
        <h3 className="font-bold">Weather</h3>
        <p>Temperature: {weather.temp}°F</p>
        <p>Condition: {weather.condition}</p>
      </div>
    );
  }
});
```

### Registering Widgets

Pass widgets to the Thread component:

```tsx
import { Thread } from '@rodger/ui';
import { WeatherWidget } from './components/WeatherWidget';
import { CartBuilderUI } from '@rodger/widgets';

<Thread
  widgets={{
    getWeather: WeatherWidget,
    cartBuilder: CartBuilderUI
  }}
/>
```

Now when your agent uses these tools, the custom widgets will render the results!

### Widget Best Practices

1. **Type Safety** - Use TypeScript for tool result types:

```tsx
interface WeatherResult {
  temp: number;
  condition: string;
  location: string;
}

render: ({ result }) => {
  const weather = result as WeatherResult;
  // Now you have full type safety
}
```

2. **Error Handling** - Handle missing or malformed data:

```tsx
render: ({ result }) => {
  if (!result || typeof result !== 'object') {
    return <div>Invalid weather data</div>;
  }
  // Render normally
}
```

3. **Loading States** - Show loading indicators:

```tsx
render: ({ result, status }) => {
  if (status === 'loading') {
    return <div>Loading weather...</div>;
  }
  // Render result
}
```

## Styling and Theming

### Tailwind Configuration

Add @rodger/ui to your Tailwind config:

```js
// tailwind.config.js
module.exports = {
  content: [
    './app/**/*.{js,ts,jsx,tsx}',
    './node_modules/@rodger/ui/dist/**/*.{js,mjs}'
  ],
  theme: {
    extend: {
      colors: {
        background: 'hsl(var(--background))',
        foreground: 'hsl(var(--foreground))',
        primary: 'hsl(var(--primary))',
        'muted-foreground': 'hsl(var(--muted-foreground))',
        border: 'hsl(var(--border))',
        accent: 'hsl(var(--accent))'
      }
    }
  }
};
```

### CSS Variables

Define your theme colors:

```css
/* app/globals.css */
:root {
  --background: 0 0% 100%;
  --foreground: 222.2 84% 4.9%;
  --primary: 221.2 83.2% 53.3%;
  --muted-foreground: 215.4 16.3% 46.9%;
  --border: 214.3 31.8% 91.4%;
  --accent: 210 40% 96.1%;
}

.dark {
  --background: 222.2 84% 4.9%;
  --foreground: 210 40% 98%;
  --primary: 217.2 91.2% 59.8%;
  --muted-foreground: 215 20.2% 65.1%;
  --border: 217.2 32.6% 17.5%;
  --accent: 217.2 32.6% 17.5%;
}
```

### Custom Styling

Override component styles with className:

```tsx
<Thread
  className="border rounded-lg shadow-lg"
/>
```

Or use Tailwind's @apply for global customization:

```css
/* Custom message styles */
.aui-user-message {
  @apply bg-blue-500 text-white rounded-2xl;
}

.aui-assistant-message {
  @apply bg-gray-100 dark:bg-gray-800 rounded-2xl;
}
```

## Advanced Usage

### With Pre-built Widgets

Combine with @rodger/widgets for instant tool UIs:

```tsx
import { Thread } from '@rodger/ui';
import { CartBuilderUI, ProductLookupUI } from '@rodger/widgets';

<Thread
  widgets={{
    cartBuilder: CartBuilderUI,
    productLookup: ProductLookupUI
  }}
/>
```

### With Custom Tools

Create tools with @rodger/tools and render with custom widgets:

```tsx
// Backend: app/api/chat/route.ts
import { createAgent } from '@rodger/core';
import { cartBuilder, productLookup } from '@rodger/tools';

const agent = createAgent({
  name: 'Shopping Agent',
  llm: { provider: 'openai', model: 'gpt-4o' },
  tools: {
    cartBuilder,
    productLookup
  }
});

// Frontend: components/Chat.tsx
import { Thread } from '@rodger/ui';
import { CartBuilderUI, ProductLookupUI } from '@rodger/widgets';

<Thread
  widgets={{
    cartBuilder: CartBuilderUI,
    productLookup: ProductLookupUI
  }}
/>
```

### Message History Management

Handle message history in your API:

```typescript
// app/api/chat/history/route.ts
export async function POST(req: Request) {
  const { sessionId } = await req.json();

  // Fetch from database
  const messages = await db.messages.findMany({
    where: { sessionId },
    orderBy: { createdAt: 'asc' }
  });

  return Response.json({ messages });
}
```

useRodgerChat automatically loads history from `{endpoint}/history`.

### Lifecycle Monitoring

Monitor chat activity with callbacks:

```tsx
const { runtime } = useRodgerChat({
  endpoint: '/api/chat',
  sessionId: 'user-123',
  onBeforeRun: (input) => {
    // Track user input
    analytics.track('message.sent', { input });
  },
  onAfterRun: (output) => {
    // Track agent response
    analytics.track('message.received', { output });
  }
});
```

### Custom Runtime Configuration

For advanced use cases, create your own runtime:

```tsx
import { useChatRuntime } from '@assistant-ui/react-ai-sdk';
import { AssistantChatTransport } from '@assistant-ui/react-ai-sdk';

const runtime = useChatRuntime({
  transport: new AssistantChatTransport({
    api: '/api/chat',
    body: { sessionId: 'user-123' }
  }),
  initialMessages: [], // Pre-load messages
  maxToolRoundtrips: 10 // Limit tool calls
});
```

## Component Reference

### Thread Props

```typescript
interface ThreadProps {
  welcomeMessage?: ReactNode;
  suggestions?: Suggestion[];
  placeholder?: string;
  widgets?: Record<string, FC<ToolUIProps>>;
  className?: string;
}

interface Suggestion {
  title: string;
  label: string;
  action: string;
}
```

### useRodgerChat Options

```typescript
interface UseRodgerChatOptions {
  endpoint: string;
  sessionId: string;
  body?: Record<string, unknown>;
  onBeforeRun?: (input: string) => void;
  onAfterRun?: (output: string) => void;
}
```

## Examples

### Basic Chat

```tsx
import { RodgerProvider, Thread, useRodgerChat } from '@rodger/ui';

function Chat() {
  const { runtime, isLoadingHistory } = useRodgerChat({
    endpoint: '/api/chat',
    sessionId: 'user-123'
  });

  if (isLoadingHistory) return <div>Loading...</div>;

  return (
    <RodgerProvider runtime={runtime}>
      <Thread />
    </RodgerProvider>
  );
}
```

### With Suggestions

```tsx
<Thread
  suggestions={[
    { title: 'Get Started', label: 'Learn the basics', action: 'How do I get started?' },
    { title: 'Examples', label: 'See examples', action: 'Show me some examples' },
    { title: 'Help', label: 'Get help', action: 'I need help' }
  ]}
/>
```

### With Custom Welcome

```tsx
<Thread
  welcomeMessage={
    <div className="space-y-4">
      <h1 className="text-5xl font-bold">Welcome to AI Chat</h1>
      <p className="text-lg text-muted-foreground">
        Ask me anything about your business.
      </p>
    </div>
  }
/>
```

### Full-Featured Example

```tsx
'use client';

import { RodgerProvider, Thread, useRodgerChat } from '@rodger/ui';
import { CartBuilderUI, ProductLookupUI } from '@rodger/widgets';

export default function ShoppingChat() {
  const { runtime, isLoadingHistory } = useRodgerChat({
    endpoint: '/api/chat',
    sessionId: 'shopping-session-123',
    body: {
      maxSteps: 10,
      temperature: 0.7
    },
    onBeforeRun: (input) => {
      console.log('User:', input);
    },
    onAfterRun: (output) => {
      console.log('Agent:', output);
    }
  });

  if (isLoadingHistory) {
    return (
      <div className="flex items-center justify-center h-screen">
        <div className="animate-spin">Loading...</div>
      </div>
    );
  }

  return (
    <RodgerProvider runtime={runtime}>
      <div className="h-screen bg-background">
        <Thread
          welcomeMessage={
            <div>
              <h1 className="text-4xl font-bold">Shopping Assistant</h1>
              <p className="mt-4 text-muted-foreground">
                I can help you find products and build your cart.
              </p>
            </div>
          }
          suggestions={[
            {
              title: 'Browse Products',
              label: 'See what we have',
              action: 'Show me your products'
            },
            {
              title: 'Build Cart',
              label: 'Start shopping',
              action: 'Help me build a cart'
            }
          ]}
          placeholder="Ask about products..."
          widgets={{
            cartBuilder: CartBuilderUI,
            productLookup: ProductLookupUI
          }}
        />
      </div>
    </RodgerProvider>
  );
}
```

## TypeScript Support

Full TypeScript support with exported types:

```typescript
import type {
  ThreadProps,
  Suggestion,
  UseRodgerChatOptions,
  UseRodgerChatReturn,
  RodgerProviderProps
} from '@rodger/ui';
```

## Related Packages

- **[@rodger/core](../core)** - Agent framework (required)
- **[@rodger/tools](../tools)** - Pre-built tools for agents
- **[@rodger/widgets](../widgets)** - Pre-built UI widgets for tools

## Documentation

- [Full Documentation](https://docs.rodger.ai)
- [Widget Creation Guide](https://docs.rodger.ai/widgets/creating-widgets)
- [API Reference](https://docs.rodger.ai/api-reference)

## License

MIT