CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-copilotkit--react-ui

Production-ready React UI components library for building deeply-integrated AI assistants and copilots

Overview
Eval results
Files

customization.mddocs/

Customization

Comprehensive customization system for tailoring the appearance and behavior of CopilotKit chat interfaces. Supports multiple levels of customization from simple (labels/icons) to advanced (complete component replacement).

Capabilities

Icons Configuration

Customize all icons used throughout the chat interface.

interface CopilotChatIcons {
  /** Icon for opening the chat (popup/sidebar button) */
  openIcon?: React.ReactNode;

  /** Icon for closing the chat (popup/sidebar button) */
  closeIcon?: React.ReactNode;

  /** Icon for header close button */
  headerCloseIcon?: React.ReactNode;

  /** Icon for send message button */
  sendIcon?: React.ReactNode;

  /** Icon for activity/loading indicator */
  activityIcon?: React.ReactNode;

  /** Spinner icon for loading states */
  spinnerIcon?: React.ReactNode;

  /** Icon for stop generation button */
  stopIcon?: React.ReactNode;

  /** Icon for regenerate response button */
  regenerateIcon?: React.ReactNode;

  /** Icon for push-to-talk microphone button */
  pushToTalkIcon?: React.ReactNode;

  /** Icon for copy to clipboard button */
  copyIcon?: React.ReactNode;

  /** Icon for positive feedback button */
  thumbsUpIcon?: React.ReactNode;

  /** Icon for negative feedback button */
  thumbsDownIcon?: React.ReactNode;

  /** Icon for upload/attach file button */
  uploadIcon?: React.ReactNode;
}

Usage Example:

import { CopilotPopup } from "@copilotkit/react-ui";
import {
  SendIcon,
  ThumbsUpIcon,
  ThumbsDownIcon,
  CopyIcon,
} from "./my-icons";

function App() {
  return (
    <CopilotPopup
      instructions="You are helpful."
      icons={{
        sendIcon: <SendIcon className="w-5 h-5" />,
        copyIcon: <CopyIcon />,
        thumbsUpIcon: <ThumbsUpIcon />,
        thumbsDownIcon: <ThumbsDownIcon />,
        regenerateIcon: "🔄",
        stopIcon: "⏹️",
      }}
    />
  );
}

Labels Configuration

Customize all text labels displayed in the chat interface.

interface CopilotChatLabels {
  /** Initial message(s) displayed when chat starts - string or array of strings */
  initial?: string | string[];

  /** Chat header title (default: "CopilotKit") */
  title?: string;

  /** Input field placeholder (default: "Type a message...") */
  placeholder?: string;

  /** Error message text (default: "❌ An error occurred. Please try again.") */
  error?: string;

  /** Stop button label (default: "Stop generating") */
  stopGenerating?: string;

  /** Regenerate button label (default: "Regenerate response") */
  regenerateResponse?: string;

  /** Copy button label (default: "Copy to clipboard") */
  copyToClipboard?: string;

  /** Thumbs up button label (default: "Thumbs up") */
  thumbsUp?: string;

  /** Thumbs down button label (default: "Thumbs down") */
  thumbsDown?: string;

  /** Copied confirmation text (default: "Copied!") */
  copied?: string;
}

Usage Example:

import { CopilotChat } from "@copilotkit/react-ui";

function App() {
  return (
    <CopilotChat
      instructions="You are a travel assistant."
      labels={{
        title: "Travel Buddy",
        initial: [
          "Welcome to Travel Buddy! 🌍",
          "I can help you plan your next adventure.",
          "Try asking about destinations, booking flights, or travel tips!",
        ],
        placeholder: "Ask about travel plans...",
        error: "Oops! Something went wrong. Please try again.",
        stopGenerating: "Stop",
        regenerateResponse: "Try again",
        copyToClipboard: "Copy",
        thumbsUp: "Helpful",
        thumbsDown: "Not helpful",
        copied: "Copied to clipboard!",
      }}
    />
  );
}

CSS Custom Properties

Type-safe CSS custom properties for comprehensive visual theming. Apply to any parent element to theme all nested CopilotKit components.

interface CopilotKitCSSProperties extends React.CSSProperties {
  /** Primary brand color */
  "--copilot-kit-primary-color"?: string;

  /** Contrast color for primary (text on primary background) */
  "--copilot-kit-contrast-color"?: string;

  /** Background color for chat panel */
  "--copilot-kit-background-color"?: string;

  /** Background color for input field */
  "--copilot-kit-input-background-color"?: string;

  /** Secondary UI color */
  "--copilot-kit-secondary-color"?: string;

  /** Contrast color for secondary (text on secondary background) */
  "--copilot-kit-secondary-contrast-color"?: string;

  /** Color for separators and borders */
  "--copilot-kit-separator-color"?: string;

  /** Color for muted/secondary text */
  "--copilot-kit-muted-color"?: string;

  /** Error background color */
  "--copilot-kit-error-background"?: string;

  /** Error border color */
  "--copilot-kit-error-border"?: string;

  /** Error text color */
  "--copilot-kit-error-text"?: string;

  /** Small shadow for subtle elevation */
  "--copilot-kit-shadow-sm"?: string;

  /** Medium shadow for moderate elevation */
  "--copilot-kit-shadow-md"?: string;

  /** Large shadow for prominent elevation */
  "--copilot-kit-shadow-lg"?: string;

  /** Dev console background color */
  "--copilot-kit-dev-console-bg"?: string;

  /** Dev console text color */
  "--copilot-kit-dev-console-text"?: string;
}

Usage Example:

import { CopilotPopup, CopilotKitCSSProperties } from "@copilotkit/react-ui";

function App() {
  const customStyles: CopilotKitCSSProperties = {
    "--copilot-kit-primary-color": "#6366f1",
    "--copilot-kit-contrast-color": "#ffffff",
    "--copilot-kit-background-color": "#f9fafb",
    "--copilot-kit-input-background-color": "#ffffff",
    "--copilot-kit-secondary-color": "#e5e7eb",
    "--copilot-kit-secondary-contrast-color": "#1f2937",
    "--copilot-kit-separator-color": "#d1d5db",
    "--copilot-kit-muted-color": "#6b7280",
    "--copilot-kit-shadow-md": "0 4px 6px -1px rgb(0 0 0 / 0.1)",
  };

  return (
    <div style={customStyles}>
      <CopilotPopup instructions="You are helpful." />
    </div>
  );
}

You can also apply CSS variables in a stylesheet:

.my-app {
  --copilot-kit-primary-color: #6366f1;
  --copilot-kit-contrast-color: #ffffff;
  --copilot-kit-background-color: #f9fafb;
  --copilot-kit-input-background-color: #ffffff;
  --copilot-kit-secondary-color: #e5e7eb;
  --copilot-kit-separator-color: #d1d5db;
  --copilot-kit-muted-color: #6b7280;
  --copilot-kit-shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
  --copilot-kit-shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1);
  --copilot-kit-shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1);
}

/* Dark mode theme */
.dark-mode {
  --copilot-kit-primary-color: #818cf8;
  --copilot-kit-contrast-color: #1e1b4b;
  --copilot-kit-background-color: #1f2937;
  --copilot-kit-input-background-color: #374151;
  --copilot-kit-secondary-color: #4b5563;
  --copilot-kit-secondary-contrast-color: #f9fafb;
  --copilot-kit-separator-color: #6b7280;
  --copilot-kit-muted-color: #9ca3af;
}

Custom Markdown Renderers

Customize how markdown elements are rendered in assistant messages.

type ComponentsMap<T extends Record<string, object> = Record<string, object>> = {
  [K in keyof T]: React.FC<{ children?: React.ReactNode } & T[K]>;
};

Usage Example:

import { CopilotChat } from "@copilotkit/react-ui";
import { Components } from "react-markdown";

function App() {
  const customRenderers: Partial<Components> = {
    // Custom heading renderer
    h1: ({ children }) => (
      <h1 className="text-3xl font-bold text-purple-600 mb-4">
        {children}
      </h1>
    ),

    // Custom link renderer with external icon
    a: ({ href, children }) => (
      <a
        href={href}
        className="text-blue-600 hover:underline"
        target="_blank"
        rel="noopener noreferrer"
      >
        {children} 🔗
      </a>
    ),

    // Custom code block with syntax highlighting wrapper
    code: ({ inline, className, children }) => {
      if (inline) {
        return (
          <code className="bg-gray-100 px-1 py-0.5 rounded text-sm">
            {children}
          </code>
        );
      }
      return (
        <pre className="bg-gray-900 text-white p-4 rounded-lg overflow-x-auto">
          <code className={className}>{children}</code>
        </pre>
      );
    },

    // Custom list renderer
    ul: ({ children }) => (
      <ul className="list-disc list-inside space-y-2 ml-4">
        {children}
      </ul>
    ),

    // Custom blockquote
    blockquote: ({ children }) => (
      <blockquote className="border-l-4 border-blue-500 pl-4 italic text-gray-700">
        {children}
      </blockquote>
    ),
  };

  return (
    <CopilotChat
      instructions="You are helpful."
      markdownTagRenderers={customRenderers}
    />
  );
}

Component Replacement

Replace default components with custom implementations for complete control over rendering.

Available Component Props:

interface CopilotChatProps {
  /** Custom assistant message component */
  AssistantMessage?: React.ComponentType<AssistantMessageProps>;

  /** Custom user message component */
  UserMessage?: React.ComponentType<UserMessageProps>;

  /** Custom error message component */
  ErrorMessage?: React.ComponentType<ErrorMessageProps>;

  /** Custom messages container component */
  Messages?: React.ComponentType<MessagesProps>;

  /** Custom message renderer */
  RenderMessage?: React.ComponentType<RenderMessageProps>;

  /** Custom suggestions list renderer */
  RenderSuggestionsList?: React.ComponentType<RenderSuggestionsListProps>;

  /** Custom input component */
  Input?: React.ComponentType<InputProps>;

  /** Custom image renderer */
  ImageRenderer?: React.ComponentType<ImageRendererProps>;
}

interface CopilotModalProps extends CopilotChatProps {
  /** Custom window/modal component */
  Window?: React.ComponentType<WindowProps>;

  /** Custom toggle button component */
  Button?: React.ComponentType<ButtonProps>;

  /** Custom header component */
  Header?: React.ComponentType<HeaderProps>;
}

Usage Example - Custom Assistant Message:

import { CopilotChat, AssistantMessageProps, Markdown } from "@copilotkit/react-ui";

function CustomAssistantMessage({
  message,
  isLoading,
  isGenerating,
  onRegenerate,
  onCopy,
}: AssistantMessageProps) {
  return (
    <div className="assistant-message">
      <div className="avatar">
        <img src="/bot-avatar.png" alt="AI" />
      </div>
      <div className="content">
        {isLoading && <div className="thinking">Thinking...</div>}
        {message && <Markdown content={message.content} />}
        {isGenerating && <span className="cursor">▊</span>}
      </div>
      <div className="actions">
        {onRegenerate && (
          <button onClick={onRegenerate}>Regenerate</button>
        )}
        {onCopy && message && (
          <button onClick={() => onCopy(message.content)}>Copy</button>
        )}
      </div>
    </div>
  );
}

function App() {
  return (
    <CopilotChat
      instructions="You are helpful."
      AssistantMessage={CustomAssistantMessage}
    />
  );
}

Usage Example - Custom Input Component:

import { CopilotChat, InputProps } from "@copilotkit/react-ui";
import { useState } from "react";

function CustomInput({ inProgress, onSend, onStop }: InputProps) {
  const [text, setText] = useState("");

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    if (!text.trim() || inProgress) return;
    await onSend(text);
    setText("");
  };

  return (
    <form onSubmit={handleSubmit} className="custom-input">
      <input
        type="text"
        value={text}
        onChange={(e) => setText(e.target.value)}
        placeholder="Type your message..."
        disabled={inProgress}
        className="input-field"
      />
      {inProgress ? (
        <button type="button" onClick={onStop} className="stop-btn">
          Stop
        </button>
      ) : (
        <button type="submit" disabled={!text.trim()} className="send-btn">
          Send
        </button>
      )}
    </form>
  );
}

function App() {
  return (
    <CopilotChat
      instructions="You are helpful."
      Input={CustomInput}
    />
  );
}

System Message Customization

Customize or disable the system message sent to the AI.

type SystemMessageFunction = (args: {
  instructions?: string;
  // Additional context
}) => string;

Usage Example:

import { CopilotChat } from "@copilotkit/react-ui";

function App() {
  const customSystemMessage = ({ instructions }) => {
    return `
You are an expert assistant with the following guidelines:
${instructions || "Be helpful and concise."}

Important rules:
- Always format code with proper syntax highlighting
- Provide step-by-step explanations for complex topics
- Ask clarifying questions when needed
- Current date: ${new Date().toLocaleDateString()}
`;
  };

  return (
    <CopilotChat
      instructions="You are a coding tutor."
      makeSystemMessage={customSystemMessage}
    />
  );
}

// Or disable system message entirely
function AppNoSystem() {
  return (
    <CopilotChat
      disableSystemMessage={true}
    />
  );
}

Image Upload Configuration

Configure image upload functionality.

interface CopilotChatProps {
  /** Enable image upload functionality */
  imageUploadsEnabled?: boolean;

  /** File input accept attribute (default: "image/*") */
  inputFileAccept?: string;
}

Usage Example:

import { CopilotChat } from "@copilotkit/react-ui";

function App() {
  return (
    <CopilotChat
      instructions="You are a helpful assistant."
      imageUploadsEnabled={true}
      inputFileAccept="image/png,image/jpeg,image/gif"
    />
  );
}

Error Handling Customization

Customize error rendering and handling.

interface CopilotChatProps {
  /** Custom error renderer for inline chat errors */
  renderError?: (error: {
    /** Error message text */
    message: string;
    /** Operation that caused the error */
    operation?: string;
    /** Error timestamp */
    timestamp: number;
    /** Callback to dismiss the error */
    onDismiss: () => void;
    /** Optional callback to retry the failed operation */
    onRetry?: () => void;
  }) => React.ReactNode;

  /** Error handler for debugging and observability */
  onError?: CopilotErrorHandler;
}

type CopilotErrorHandler = (error: Error) => void;

Usage Example:

import { CopilotChat } from "@copilotkit/react-ui";

function App() {
  return (
    <CopilotChat
      instructions="You are helpful."
      renderError={({ message, operation, onDismiss, onRetry }) => (
        <div className="custom-error-banner">
          <div className="error-content">
            <h3>⚠️ Error</h3>
            <p>{message}</p>
            {operation && <small>During: {operation}</small>}
          </div>
          <div className="error-actions">
            {onRetry && (
              <button onClick={onRetry} className="retry-btn">
                Retry
              </button>
            )}
            <button onClick={onDismiss} className="dismiss-btn">
              Dismiss
            </button>
          </div>
        </div>
      )}
      onError={(error) => {
        console.error("Chat error:", error);
        // Send to monitoring service
      }}
    />
  );
}

Alternative Example - Simple Banner:

function App() {
  return (
    <CopilotChat
      renderError={({ message, onDismiss }) => (
        <div className="simple-error">
          <span>{message}</span>
          <button onClick={onDismiss}>×</button>
        </div>
      )}
    />
  );
}

Component Replacement

Replace default UI components with custom implementations. Empty interfaces for Button, Header, and Window are intentionally extensible to allow custom props.

interface CopilotModalProps {
  /** Custom window/modal wrapper */
  Window?: React.ComponentType<WindowProps>;

  /** Custom toggle button */
  Button?: React.ComponentType<ButtonProps>;

  /** Custom header */
  Header?: React.ComponentType<HeaderProps>;
}

interface WindowProps {
  clickOutsideToClose: boolean;
  hitEscapeToClose: boolean;
  shortcut: string;
  children?: React.ReactNode;
}

/** Extensible interface for custom button implementations */
interface ButtonProps {
  // Empty - extend with your own props
}

/** Extensible interface for custom header implementations */
interface HeaderProps {
  // Empty - extend with your own props
}

Usage Example:

import { CopilotPopup, useChatContext } from "@copilotkit/react-ui";

function CustomButton() {
  const { open, setOpen, icons } = useChatContext();
  return (
    <button
      onClick={() => setOpen(!open)}
      className="my-custom-button"
    >
      {open ? icons.closeIcon : icons.openIcon}
    </button>
  );
}

function App() {
  return (
    <CopilotPopup
      Button={CustomButton}
      instructions="You are helpful."
    />
  );
}

Markdown Component Renderers

Customize how specific markdown elements are rendered within AI messages.

type ComponentsMap<T extends Record<string, object> = Record<string, object>> = {
  [K in keyof T]: React.FC<{ children?: React.ReactNode } & T[K]>;
};

interface CopilotChatProps {
  /** Custom renderers for markdown elements */
  markdownTagRenderers?: ComponentsMap;
}

Usage Example:

import { CopilotChat } from "@copilotkit/react-ui";

function App() {
  return (
    <CopilotChat
      markdownTagRenderers={{
        a: ({ href, children }) => (
          <a
            href={href}
            target="_blank"
            rel="noopener noreferrer"
            className="custom-link"
          >
            {children} 🔗
          </a>
        ),
        code: ({ className, children }) => {
          const isInline = !className;
          return isInline ? (
            <code className="inline-code">{children}</code>
          ) : (
            <code className={className}>{children}</code>
          );
        },
        reference: ({ id, children }) => (
          <span className="reference-tag" data-ref-id={id}>
            {children}
          </span>
        ),
      }}
    />
  );
}

Example - Custom Reference Tags:

function App() {
  return (
    <CopilotChat
      instructions="Use <reference id='doc-123'>document name</reference> tags to cite sources."
      markdownTagRenderers={{
        reference: ({ id, children }: { id: string; children: React.ReactNode }) => (
          <button
            onClick={() => openDocument(id)}
            className="doc-reference"
          >
            📄 {children}
          </button>
        ),
      }}
    />
  );
}

Full Component Customization

Replace entire component implementations for complete control.

interface CopilotChatProps {
  /** Custom assistant message component */
  AssistantMessage?: React.ComponentType<AssistantMessageProps>;

  /** Custom user message component */
  UserMessage?: React.ComponentType<UserMessageProps>;

  /** Custom error message component */
  ErrorMessage?: React.ComponentType<ErrorMessageProps>;

  /** Custom messages container */
  Messages?: React.ComponentType<MessagesProps>;

  /** Custom input component */
  Input?: React.ComponentType<InputProps>;

  /** Custom message renderer (break-glass for full control) */
  RenderMessage?: React.ComponentType<RenderMessageProps>;

  /** Custom suggestions list */
  RenderSuggestionsList?: React.ComponentType<RenderSuggestionsListProps>;

  /** Custom image renderer */
  ImageRenderer?: React.ComponentType<ImageRendererProps>;
}

Usage Example - Custom Input:

import { CopilotChat } from "@copilotkit/react-ui";
import type { InputProps } from "@copilotkit/react-ui";
import { useState } from "react";

function CustomInput({ inProgress, onSend, onStop }: InputProps) {
  const [text, setText] = useState("");
  const [attachments, setAttachments] = useState<File[]>([]);

  const handleSubmit = async () => {
    if (!text.trim()) return;
    await onSend(text);
    setText("");
    setAttachments([]);
  };

  return (
    <div className="custom-input">
      <div className="attachments">
        {attachments.map((file, i) => (
          <span key={i}>{file.name}</span>
        ))}
      </div>
      <textarea
        value={text}
        onChange={(e) => setText(e.target.value)}
        placeholder="Type your message..."
        disabled={inProgress}
      />
      <div className="actions">
        <input
          type="file"
          onChange={(e) => setAttachments([...attachments, ...e.target.files])}
        />
        {inProgress ? (
          <button onClick={onStop}>Stop</button>
        ) : (
          <button onClick={handleSubmit}>Send</button>
        )}
      </div>
    </div>
  );
}

function App() {
  return <CopilotChat Input={CustomInput} />;
}

Complete Theming Example

Combining multiple customization approaches for a fully themed interface:

import {
  CopilotPopup,
  CopilotKitCSSProperties,
  AssistantMessageProps,
  Markdown,
} from "@copilotkit/react-ui";

// Custom styled assistant message
function BrandedAssistantMessage({
  message,
  isLoading,
  onCopy,
}: AssistantMessageProps) {
  return (
    <div className="branded-message">
      <div className="avatar">
        <img src="/brand-logo.png" alt="Assistant" />
      </div>
      <div className="content">
        {isLoading ? (
          <div className="pulse">Thinking...</div>
        ) : (
          message && <Markdown content={message.content} />
        )}
      </div>
      {message && onCopy && (
        <button onClick={() => onCopy(message.content)}>📋</button>
      )}
    </div>
  );
}

function App() {
  const brandTheme: CopilotKitCSSProperties = {
    "--copilot-kit-primary-color": "#FF6B6B",
    "--copilot-kit-contrast-color": "#FFFFFF",
    "--copilot-kit-background-color": "#FFF5F5",
    "--copilot-kit-secondary-color": "#FFE5E5",
    "--copilot-kit-separator-color": "#FFD0D0",
  };

  return (
    <div style={brandTheme}>
      <CopilotPopup
        instructions="You are our branded assistant."
        labels={{
          title: "BrandBot",
          initial: "Welcome! I'm here to help.",
          placeholder: "Ask me anything...",
        }}
        icons={{
          sendIcon: "➤",
          regenerateIcon: "🔄",
          copyIcon: "📋",
          thumbsUpIcon: "👍",
          thumbsDownIcon: "👎",
        }}
        AssistantMessage={BrandedAssistantMessage}
      />
    </div>
  );
}

Install with Tessl CLI

npx tessl i tessl/npm-copilotkit--react-ui@1.10.1

docs

chat-components.md

customization.md

dev-console.md

hooks.md

index.md

message-components.md

types.md

README.md

tile.json