
Research
Supply Chain Attack on Axios Pulls Malicious Dependency from npm
A supply chain attack on Axios introduced a malicious dependency, plain-crypto-js@4.2.1, published minutes earlier and absent from the project’s GitHub releases.
@rodgerai/ui
Advanced tools
Beautiful, customizable React components for building AI chat interfaces powered by Rodger.ai agents.
npm install @rodger/ui @rodger/core
# or
pnpm add @rodger/ui @rodger/core
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.
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
});
}
'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.
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 classesContext 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 componentsFeatures:
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 respondsReturns:
runtime (AssistantRuntime) - Runtime instance for RodgerProviderisLoadingHistory (boolean) - Loading state for message historyFeatures:
{endpoint}/historyLow-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.
Widgets render tool results with custom UI components.
// 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>
);
}
});
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!
interface WeatherResult {
temp: number;
condition: string;
location: string;
}
render: ({ result }) => {
const weather = result as WeatherResult;
// Now you have full type safety
}
render: ({ result }) => {
if (!result || typeof result !== 'object') {
return <div>Invalid weather data</div>;
}
// Render normally
}
render: ({ result, status }) => {
if (status === 'loading') {
return <div>Loading weather...</div>;
}
// Render result
}
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))'
}
}
}
};
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%;
}
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;
}
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
}}
/>
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
}}
/>
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.
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 });
}
});
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
});
interface ThreadProps {
welcomeMessage?: ReactNode;
suggestions?: Suggestion[];
placeholder?: string;
widgets?: Record<string, FC<ToolUIProps>>;
className?: string;
}
interface Suggestion {
title: string;
label: string;
action: string;
}
interface UseRodgerChatOptions {
endpoint: string;
sessionId: string;
body?: Record<string, unknown>;
onBeforeRun?: (input: string) => void;
onAfterRun?: (output: string) => void;
}
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>
);
}
<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' }
]}
/>
<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>
}
/>
'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>
);
}
Full TypeScript support with exported types:
import type {
ThreadProps,
Suggestion,
UseRodgerChatOptions,
UseRodgerChatReturn,
RodgerProviderProps
} from '@rodger/ui';
MIT
FAQs
React UI components for Rodger.ai agents
We found that @rodgerai/ui demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Research
A supply chain attack on Axios introduced a malicious dependency, plain-crypto-js@4.2.1, published minutes earlier and absent from the project’s GitHub releases.

Research
Malicious versions of the Telnyx Python SDK on PyPI delivered credential-stealing malware via a multi-stage supply chain attack.

Security News
TeamPCP is partnering with ransomware group Vect to turn open source supply chain attacks on tools like Trivy and LiteLLM into large-scale ransomware operations.