@rodgerai/ui
v2.0.4
Published
React UI components for Rodger.ai agents
Maintainers
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/corePeer 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 contentsuggestions(Suggestion[]) - Suggestion chips for quick actionsplaceholder(string) - Input placeholder textwidgets(Record<string, FC>) - Custom widgets for tool resultsclassName(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 useRodgerChatchildren(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 chatsessionId(string) - Unique session ID for conversation historybody(object, optional) - Additional parameters sent with each requestonBeforeRun(function, optional) - Called before agent processes inputonAfterRun(function, optional) - Called after agent responds
Returns:
runtime(AssistantRuntime) - Runtime instance for RodgerProviderisLoadingHistory(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
- 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
}- Error Handling - Handle missing or malformed data:
render: ({ result }) => {
if (!result || typeof result !== 'object') {
return <div>Invalid weather data</div>;
}
// Render normally
}- 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
- @rodger/core - Agent framework (required)
- @rodger/tools - Pre-built tools for agents
- @rodger/widgets - Pre-built UI widgets for tools
Documentation
License
MIT
