Skip to content

Latest commit

 

History

History
621 lines (483 loc) · 11 KB

File metadata and controls

621 lines (483 loc) · 11 KB

API Reference

Complete API documentation for React Agent UI.

Components

ConversationUI

Main component for rendering conversation events.

interface ConversationUIProps {
  events: UIEvent[];
  renderers?: Partial<RendererRegistry>;
  onAction?: (actionId: string, eventId: string, data?: Record<string, unknown>) => void;
  messageContainer?: React.ComponentType<MessageContainerProps>;
  className?: string;
  autoScroll?: boolean;
  emptyComponent?: React.ComponentType;
  debug?: boolean;
}

Props:

  • events (required): Array of UIEvent objects to render
  • renderers: Override or add custom renderers (optional)
  • onAction: Callback when action is triggered (optional)
  • messageContainer: Custom message wrapper component (optional)
  • className: Additional CSS class for root element (optional)
  • autoScroll: Auto-scroll to latest message (default: true)
  • emptyComponent: Component to show when no messages (optional)
  • debug: Enable console logging (default: false)

Example:

import { ConversationUI } from "@react-agent-ui/core";

<ConversationUI
  events={messages}
  onAction={(actionId, eventId, data) => {
    console.log("Action:", actionId);
  }}
  autoScroll
/>

MessageContainer

Wrapper component for individual messages.

interface MessageContainerProps {
  role?: MessageRole;
  children: React.ReactNode;
  className?: string;
  timestamp?: number;
}

Props:

  • role: Message role for styling ("user" | "assistant" | "system" | "tool")
  • children: Message content
  • className: Additional CSS class (optional)
  • timestamp: Unix timestamp for display (optional)

Example:

const CustomContainer = ({ role, children, timestamp }) => (
  <div className={`msg-${role}`}>
    {children}
    {timestamp && <span>{new Date(timestamp).toLocaleString()}</span>}
  </div>
);

<ConversationUI
  events={events}
  messageContainer={CustomContainer}
/>

Hooks

useConversationEvents

Hook for managing conversation state and events.

function useConversationEvents(
  options?: { debug?: boolean }
): UseConversationEventsReturn

Returns:

interface UseConversationEventsReturn {
  state: ConversationState;
  addEvent: (event: UIEvent) => void;
  updateEvent: (update: MessageUpdate) => void;
  removeEvent: (eventId: string) => void;
  setLoading: (loading: boolean) => void;
  setError: (error: string | undefined) => void;
  clear: () => void;
}

Methods:

  • addEvent: Add a new event/message
  • updateEvent: Update existing event (append/replace/partial)
  • removeEvent: Remove event by ID
  • setLoading: Set loading state
  • setError: Set error message
  • clear: Clear all messages

Example:

const { state, addEvent, updateEvent } = useConversationEvents();

// Add message
addEvent({
  id: "msg-1",
  role: "user",
  kind: "text",
  payload: { content: "Hello" }
});

// Stream update
updateEvent({
  eventId: "msg-1",
  operation: "append",
  payload: " World"
});

// Use in render
<ConversationUI events={state.messages.map(m => m.event)} />

useActionHandler

Hook for handling action responses.

function useActionHandler(
  options?: { onResponse?: (response: ActionResponse) => void; debug?: boolean }
): UseActionHandlerReturn

Returns:

interface UseActionHandlerReturn {
  handleAction: (actionId: string, eventId: string, context?: Record<string, unknown>) => void;
  handleActionResponse: (response: ActionResponse) => void;
}

Methods:

  • handleAction: Process action trigger with metadata
  • handleActionResponse: Send raw action response

Example:

const { handleAction } = useActionHandler({
  onResponse: async (response) => {
    await fetch("/api/actions", {
      method: "POST",
      body: JSON.stringify(response)
    });
  }
});

<ConversationUI
  events={events}
  onAction={(actionId, eventId, data) => {
    handleAction(actionId, eventId, { custom: data });
  }}
/>

Types

UIEvent

Base event interface.

interface UIEvent {
  id: string;
  role?: "system" | "user" | "assistant" | "tool";
  kind: string;
  payload: unknown;
  meta?: Record<string, unknown>;
}

TypedUIEvent

Type-safe event with discriminated kind.

interface TypedUIEvent<K extends EventKind = EventKind, P = unknown>
  extends Omit<UIEvent, "kind" | "payload"> {
  kind: K;
  payload: P;
}

Example:

const textEvent: TypedUIEvent<"text", TextPayload> = {
  id: "1",
  kind: "text",
  payload: { content: "Hello" }
};

MessageUpdate

Streaming update operation.

interface MessageUpdate {
  eventId: string;
  operation: "append" | "replace" | "partial";
  payload: unknown;
  timestamp?: number;
}

Operations:

  • append: Add content (strings concat, arrays merge)
  • replace: Replace entire payload
  • partial: Merge object properties

ActionResponse

Response sent when action is triggered.

interface ActionResponse {
  actionId: string;
  eventId: string;
  context?: Record<string, unknown>;
  timestamp: number;
}

Payload Types

TextPayload

interface TextPayload {
  content: string;
}

MarkdownPayload

interface MarkdownPayload {
  content: string;
}

ImagePayload

interface ImagePayload {
  src: string;
  alt?: string;
  width?: number;
  height?: number;
  caption?: string;
}

TablePayload

interface TablePayload {
  headers: string[];
  rows: (string | number | boolean)[][];
  title?: string;
  sortable?: boolean;
}

ChartPayload

interface ChartPayload {
  type: "line" | "bar" | "pie" | "scatter" | "area";
  data: Record<string, unknown>[];
  xAxis?: string;
  yAxis?: string;
  title?: string;
  config?: Record<string, unknown>;
}

ActionPayload

interface ActionPayload {
  id: string;
  label: string;
  variant?: "primary" | "secondary" | "danger" | "default";
  description?: string;
  data?: Record<string, unknown>;
}

LoadingPayload

interface LoadingPayload {
  message?: string;
}

ErrorPayload

interface ErrorPayload {
  message: string;
  code?: string;
  details?: unknown;
}

Renderers

Default Renderers

  • TextRenderer - Plain text
  • MarkdownRenderer - Markdown content
  • ImageRenderer - Images with captions
  • TableRenderer - Tabular data
  • ChartRenderer - Data charts
  • ActionRenderer - Interactive buttons
  • LoadingRenderer - Loading state
  • ErrorRenderer - Error messages
  • FallbackRenderer - Unknown event kinds

RendererProps

interface RendererProps<E extends UIEvent = UIEvent> {
  event: E;
  onAction?: (actionId: string, data?: Record<string, unknown>) => void;
  className?: string;
  style?: React.CSSProperties;
}

Custom Renderer Example

const CustomRenderer: React.FC<RendererProps> = ({ event, onAction, className }) => {
  return (
    <div className={`custom-${event.kind} ${className}`}>
      {/* render based on event.payload */}
    </div>
  );
};

<ConversationUI
  events={events}
  renderers={{ custom: CustomRenderer }}
/>

State Management

conversationReducer

Pure reducer for conversation state.

function conversationReducer(
  state: ConversationState,
  action: ConversationAction
): ConversationState

Actions:

  • ADD_MESSAGE - Add new event
  • UPDATE_MESSAGE - Update with streaming operation
  • REMOVE_MESSAGE - Remove by ID
  • SET_LOADING - Set loading state
  • SET_ERROR - Set error
  • CLEAR_MESSAGES - Clear all

ConversationState

interface ConversationState {
  messages: Message[];
  loading: boolean;
  error?: string;
}

interface Message {
  event: UIEvent;
  version: number;
  timestamp: number;
  isStreaming: boolean;
  error?: string;
}

CSS Classes

Message Containers

  • .rai-message-container
  • .rai-message-user
  • .rai-message-assistant
  • .rai-message-system
  • .rai-message-tool
  • .rai-message-content
  • .rai-message-timestamp

Renderers

  • .rai-text-renderer
  • .rai-markdown-renderer
  • .rai-image-renderer
  • .rai-table-renderer
  • .rai-chart-renderer
  • .rai-action-renderer
  • .rai-loading-renderer
  • .rai-error-renderer
  • .rai-fallback-renderer

Actions

  • .rai-action-button
  • .rai-action-primary
  • .rai-action-secondary
  • .rai-action-danger
  • .rai-action-default

See styles.css for complete styling reference.


Constants

DEFAULT_RENDERERS

Registry of all default renderers.

const DEFAULT_RENDERERS: RendererRegistry = {
  text: TextRenderer,
  markdown: MarkdownRenderer,
  image: ImageRenderer,
  chart: ChartRenderer,
  table: TableRenderer,
  action: ActionRenderer,
  loading: LoadingRenderer,
  error: ErrorRenderer,
}

FALLBACK_RENDERER

Renderer for unknown event kinds.

const FALLBACK_RENDERER = FallbackRenderer;

Events Flow

Event Lifecycle

UIEvent created
  ↓
Added to ConversationUI via events prop
  ↓
conversationReducer processes ADD_MESSAGE
  ↓
Message stored in state
  ↓
Renderer component selected and rendered
  ↓
Message displayed

Streaming Update Flow

MessageUpdate created
  ↓
updateEvent() called
  ↓
conversationReducer processes UPDATE_MESSAGE
  ↓
Reducer applies operation (append/replace/partial)
  ↓
Message version incremented
  ↓
React re-renders updated message

Action Flow

User clicks action button
  ↓
ActionRenderer.handleClick()
  ↓
onAction callback triggered
  ↓
handleAction() creates ActionResponse
  ↓
onResponse callback with ActionResponse
  ↓
Application sends to backend

Tips & Patterns

Pattern: Streaming Responses

const { addEvent, updateEvent } = useConversationEvents();

async function streamResponse() {
  const msgId = `msg-${Date.now()}`;
  addEvent({
    id: msgId,
    role: "assistant",
    kind: "text",
    payload: { content: "" }
  });

  const response = await fetch("/api/chat", { method: "POST" });
  const reader = response.body?.getReader();

  while (true) {
    const { value, done } = await reader?.read() || {};
    if (done) break;

    const chunk = new TextDecoder().decode(value);
    updateEvent({
      eventId: msgId,
      operation: "append",
      payload: chunk
    });
  }
}

Pattern: Form with Actions

const events: UIEvent[] = [
  { id: "q1", role: "assistant", kind: "text", payload: { content: "Name?" } },
  { id: "input-name", role: "assistant", kind: "action", payload: {
    id: "submit-name",
    label: "Submit",
    variant: "primary"
  }}
];

Pattern: Custom Event Types

interface CodePlaygroundPayload {
  language: string;
  code: string;
}

const event: TypedUIEvent<"code-playground", CodePlaygroundPayload> = {
  id: "1",
  kind: "code-playground",
  payload: { language: "typescript", code: "console.log('hello')" }
};