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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@rodgerai/ui

v2.0.4

Published

React UI components for Rodger.ai agents

Readme

@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

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

Peer Dependencies

This package requires:

{
  "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:

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

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

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.

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.

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.

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

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

See @assistant-ui/react documentation for full details.

Custom Widgets

Widgets render tool results with custom UI components.

Creating a Widget

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

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:
interface WeatherResult {
  temp: number;
  condition: string;
  location: string;
}

render: ({ result }) => {
  const weather = result as WeatherResult;
  // Now you have full type safety
}
  1. Error Handling - Handle missing or malformed data:
render: ({ result }) => {
  if (!result || typeof result !== 'object') {
    return <div>Invalid weather data</div>;
  }
  // Render normally
}
  1. Loading States - Show loading indicators:
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:

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

/* 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:

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

Or use Tailwind's @apply for global customization:

/* 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:

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:

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

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

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:

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

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

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

Examples

Basic Chat

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

<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

<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

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

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

Related Packages

Documentation

License

MIT