Production-ready React UI components library for building deeply-integrated AI assistants and copilots
Default and customizable components for rendering different message types in the chat interface. These components can be used as-is or replaced with custom implementations.
Default component for rendering AI assistant messages. Displays markdown content, handles loading states, and provides message controls.
/**
* Default assistant message rendering component
* @param props - Assistant message configuration and callbacks
* @returns Rendered assistant message with controls
*/
function AssistantMessage(props: AssistantMessageProps): JSX.Element;
interface AssistantMessageProps {
/** The AI message content */
message?: AIMessage;
/** Whether this is the most recent message */
isCurrentMessage?: boolean;
/** Whether response is loading (thinking state) */
isLoading: boolean;
/** Whether response is actively streaming */
isGenerating: boolean;
/** Callback to regenerate this message */
onRegenerate?: () => void;
/** Callback when message is copied */
onCopy?: (message: string) => void;
/** Callback for positive feedback */
onThumbsUp?: (message: Message) => void;
/** Callback for negative feedback */
onThumbsDown?: (message: Message) => void;
/** Custom markdown component renderers */
markdownTagRenderers?: ComponentsMap;
/** Custom image renderer component */
ImageRenderer?: React.ComponentType<ImageRendererProps>;
/**
* @deprecated Use message property instead
* Note: Despite being deprecated, this property is currently REQUIRED
*/
rawData: any;
/** @deprecated Use message.generativeUI() instead */
subComponent?: React.JSX.Element;
}
interface AIMessage {
id: string;
role: "assistant";
content: string;
generativeUI?: () => React.ReactNode;
agentName?: string;
state?: any;
image?: ImageData;
}Usage Example:
import { AssistantMessage } from "@copilotkit/react-ui";
function CustomChat() {
const message = {
id: "msg-1",
role: "assistant" as const,
content: "Here's how to implement that feature:\n\n```typescript\nconst result = compute();\n```",
};
return (
<AssistantMessage
message={message}
rawData={message} // Required despite being deprecated
isCurrentMessage={true}
isLoading={false}
isGenerating={false}
onRegenerate={() => console.log("Regenerate")}
onCopy={(text) => navigator.clipboard.writeText(text)}
onThumbsUp={(msg) => console.log("Liked:", msg.id)}
onThumbsDown={(msg) => console.log("Disliked:", msg.id)}
/>
);
}Default component for rendering user messages. Handles both text messages and messages with image attachments.
/**
* Default user message rendering component
* @param props - User message content and image renderer
* @returns Rendered user message
*/
function UserMessage(props: UserMessageProps): JSX.Element;
interface UserMessageProps {
/** The user message content */
message?: UserMessage;
/** Image renderer component for displaying attachments */
ImageRenderer: React.ComponentType<ImageRendererProps>;
/**
* @deprecated Use message property instead
* Note: Despite being deprecated, this property is currently REQUIRED
*/
rawData: any;
}
interface UserMessage {
id: string;
role: "user";
content: string;
image?: ImageData;
}Usage Example:
import { UserMessage, ImageRenderer } from "@copilotkit/react-ui";
function CustomChat() {
const message = {
id: "msg-2",
role: "user" as const,
content: "Can you help me with this diagram?",
image: {
format: "png",
bytes: "base64-encoded-image-data",
},
};
return (
<UserMessage
message={message}
rawData={message} // Required despite being deprecated
ImageRenderer={ImageRenderer}
/>
);
}Default component for rendering images in messages. Displays base64-encoded images with error handling.
/**
* Default image rendering component
* @param props - Image data and optional text content
* @returns Rendered image element
*/
function ImageRenderer(props: ImageRendererProps): JSX.Element;
interface ImageRendererProps {
/** Image data with format and encoded bytes */
image: ImageData;
/** Optional text content to display with image */
content?: string;
/** Additional CSS class */
className?: string;
}
interface ImageData {
/** Image format (e.g., "png", "jpeg", "gif") */
format: string;
/** Base64-encoded image bytes */
bytes: string;
}Usage Example:
import { ImageRenderer } from "@copilotkit/react-ui";
function CustomImageDisplay() {
const imageData = {
format: "png",
bytes: "iVBORw0KGgoAAAANSUhEUgAAAAUA...", // base64 data
};
return (
<ImageRenderer
image={imageData}
content="Screenshot of the error"
className="my-custom-image"
/>
);
}Markdown rendering component using react-markdown with support for GitHub Flavored Markdown, math equations, and custom renderers.
/**
* Markdown rendering component with GFM and math support
* @param props - Markdown content and custom component renderers
* @returns Rendered markdown content
*/
function Markdown(props: MarkdownProps): JSX.Element;
interface MarkdownProps {
/** Markdown content string to render */
content: string;
/** Custom component renderers for markdown elements */
components?: ComponentsMap;
}
type ComponentsMap<T extends Record<string, object> = Record<string, object>> = {
[K in keyof T]: React.FC<{ children?: React.ReactNode } & T[K]>;
};Usage Example:
import { Markdown } from "@copilotkit/react-ui";
function CustomMarkdownDisplay() {
const content = `
# Title
Here's some **bold** and *italic* text.
\`\`\`typescript
const greeting = "Hello, World!";
console.log(greeting);
\`\`\`
- List item 1
- List item 2
| Column 1 | Column 2 |
|----------|----------|
| Data 1 | Data 2 |
`;
return (
<Markdown
content={content}
components={{
h1: ({ children }) => (
<h1 className="custom-heading">{children}</h1>
),
code: ({ children }) => (
<code className="custom-code">{children}</code>
),
}}
/>
);
}Props interfaces for creating fully custom message rendering components.
interface RenderMessageProps {
/** The message to render */
message: Message;
/** Whether chat is currently in progress */
inProgress: boolean;
/** Message index in the array */
index: number;
/** Whether this is the most recent message */
isCurrentMessage: boolean;
/** Result from action execution if applicable */
actionResult?: string;
/** Assistant message component to use */
AssistantMessage?: React.ComponentType<AssistantMessageProps>;
/** User message component to use */
UserMessage?: React.ComponentType<UserMessageProps>;
/** Image renderer component to use */
ImageRenderer?: React.ComponentType<ImageRendererProps>;
/** Callback to regenerate message */
onRegenerate?: (messageId: string) => void;
/** Callback when message is copied */
onCopy?: (message: string) => void;
/** Callback for positive feedback */
onThumbsUp?: (message: Message) => void;
/** Callback for negative feedback */
onThumbsDown?: (message: Message) => void;
/** Custom markdown renderers */
markdownTagRenderers?: ComponentsMap;
}
type Message = AIMessage | UserMessage;Usage Example:
import { CopilotChat } from "@copilotkit/react-ui";
import type { RenderMessageProps } from "@copilotkit/react-ui";
function CustomMessageRenderer({
message,
isCurrentMessage,
AssistantMessage,
UserMessage,
ImageRenderer,
...props
}: RenderMessageProps) {
// Add custom wrapper or logic
return (
<div className="custom-message-wrapper">
<div className="message-metadata">
<span>{new Date(message.timestamp).toLocaleTimeString()}</span>
</div>
{message.role === "assistant" && AssistantMessage && (
<AssistantMessage
message={message}
rawData={message} // Required despite being deprecated
isCurrentMessage={isCurrentMessage}
ImageRenderer={ImageRenderer}
{...props}
/>
)}
{message.role === "user" && UserMessage && (
<UserMessage
message={message}
rawData={message} // Required despite being deprecated
ImageRenderer={ImageRenderer}
/>
)}
</div>
);
}
function App() {
return (
<CopilotChat
RenderMessage={CustomMessageRenderer}
/>
);
}Interface for rendering error messages in the chat.
interface ErrorMessageProps {
/** Error information */
error: ChatError;
/** Whether this is the most recent message */
isCurrentMessage?: boolean;
/** Callback to regenerate/retry */
onRegenerate?: () => void;
/** Callback when error is copied */
onCopy?: (message: string) => void;
}
interface ChatError {
/** Error message text */
message: string;
/** Operation that caused the error */
operation?: string;
/** Error timestamp */
timestamp: number;
}Usage Example:
import { CopilotChat } from "@copilotkit/react-ui";
import type { ErrorMessageProps } from "@copilotkit/react-ui";
function CustomErrorMessage({
error,
onRegenerate,
}: ErrorMessageProps) {
return (
<div className="custom-error">
<div className="error-icon">⚠️</div>
<div className="error-content">
<h4>Something went wrong</h4>
<p>{error.message}</p>
{error.operation && <small>During: {error.operation}</small>}
</div>
<button onClick={onRegenerate}>Retry</button>
</div>
);
}
function App() {
return (
<CopilotChat
ErrorMessage={CustomErrorMessage}
/>
);
}Components for rendering chat suggestions.
/**
* Default suggestions list renderer
* @param props - Suggestions array and click handler
* @returns Rendered suggestions list
*/
function RenderSuggestionsList(props: RenderSuggestionsListProps): JSX.Element;
interface RenderSuggestionsListProps {
/** Array of suggestion items */
suggestions: CopilotChatSuggestion[];
/** Click handler for suggestions */
onSuggestionClick: (message: string) => void;
}
interface CopilotChatSuggestion {
/** Suggestion title/text displayed to user */
title: string;
/** Message to send when suggestion is clicked */
message: string;
/** Whether suggestion is still being generated */
partial?: boolean;
/** Optional CSS class */
className?: string;
}Usage Example:
import { CopilotChat } from "@copilotkit/react-ui";
import type { RenderSuggestionsListProps } from "@copilotkit/react-ui";
function CustomSuggestionsList({
suggestions,
onSuggestionClick,
}: RenderSuggestionsListProps) {
return (
<div className="custom-suggestions">
<h4>Try asking:</h4>
<div className="suggestion-grid">
{suggestions.map((suggestion, i) => (
<button
key={i}
onClick={() => onSuggestionClick(suggestion.message)}
disabled={suggestion.partial}
className={suggestion.className}
>
{suggestion.title}
{suggestion.partial && " ..."}
</button>
))}
</div>
</div>
);
}
function App() {
return (
<CopilotChat
suggestions={[
{ title: "Analyze data", message: "Can you analyze my dataset?" },
{ title: "Generate report", message: "Generate a summary report" },
{ title: "Export results", message: "How do I export the results?" },
]}
RenderSuggestionsList={CustomSuggestionsList}
/>
);
}Individual suggestion button component. Exported for advanced customization when building custom suggestion lists.
Note: This component is exported as RenderSuggestion from the package (the internal component is named Suggestion but exported with the alias RenderSuggestion).
/**
* Individual suggestion button component
* @param props - Suggestion data and click handler
* @returns Rendered suggestion button
*/
function RenderSuggestion(props: SuggestionProps): JSX.Element | null;
interface SuggestionProps {
/** Suggestion title to display */
title: string;
/** Message to send when clicked */
message: string;
/** Whether suggestion is still loading */
partial?: boolean;
/** Optional CSS class */
className?: string;
/** Click handler */
onClick: () => void;
}Usage Example:
import { RenderSuggestion } from "@copilotkit/react-ui";
import type { RenderSuggestionsListProps } from "@copilotkit/react-ui";
function CustomSuggestionsList({
suggestions,
onSuggestionClick,
}: RenderSuggestionsListProps) {
return (
<div className="custom-suggestions-layout">
{suggestions.map((suggestion, index) => (
<RenderSuggestion
key={index}
title={suggestion.title}
message={suggestion.message}
partial={suggestion.partial}
className={suggestion.className}
onClick={() => onSuggestionClick(suggestion.message)}
/>
))}
</div>
);
}Interface for customizing the messages container that wraps all messages.
interface MessagesProps {
/** Array of all messages */
messages: Message[];
/** Whether chat is in progress */
inProgress: boolean;
/** Child elements (typically suggestions) */
children?: React.ReactNode;
/** Current chat error if any */
chatError?: ChatError | null;
/** Assistant message component */
AssistantMessage: React.ComponentType<AssistantMessageProps>;
/** User message component */
UserMessage: React.ComponentType<UserMessageProps>;
/** Error message component */
ErrorMessage?: React.ComponentType<ErrorMessageProps>;
/** Message renderer */
RenderMessage: React.ComponentType<RenderMessageProps>;
/** Image renderer */
ImageRenderer: React.ComponentType<ImageRendererProps>;
/** Regenerate callback */
onRegenerate?: (messageId: string) => void;
/** Copy callback */
onCopy?: (message: string) => void;
/** Thumbs up callback */
onThumbsUp?: (message: Message) => void;
/** Thumbs down callback */
onThumbsDown?: (message: Message) => void;
/** Custom markdown renderers */
markdownTagRenderers?: ComponentsMap;
}Interface for customizing the chat input component.
interface InputProps {
/** Whether chat is in progress */
inProgress: boolean;
/** Handler for sending messages */
onSend: (text: string) => Promise<Message>;
/** Whether input is visible */
isVisible?: boolean;
/** Stop generation handler */
onStop?: () => void;
/** Upload handler (file picker trigger) */
onUpload?: () => void;
/** Hide stop button */
hideStopButton?: boolean;
}Install with Tessl CLI
npx tessl i tessl/npm-copilotkit--react-ui@1.10.1