A2UI Launched: Full CopilotKit support at launch!

A2UI Launched: CopilotKit has partnered with Google to deliver full support in both CopilotKit and AG-UI!

Check it out
LogoLogo
  • Overview
  • Integrations
  • API Reference
  • Copilot Cloud
Slanted end borderSlanted end border
Slanted start borderSlanted start border
Select integration...

Please select an integration to view the sidebar content.

Fully Headless UI
CopilotKitPremium

Fully customize your Copilot's UI from the ground up using headless UI

Early Access feature

The Fully Headless UI extension is in Early Access to CopilotKit Premium users. As it matures, it will become available as part of the core framework.

  • Get started with your public license key from Copilot Cloud
  • Or read more about Premium Features.

Overview

CopilotKit offers fully headless UI through the useCopilotChatHeadless_c hook. By using this hook, you can build your own chat interfaces from the ground up while still utilizing CopilotKit's core features and ease-of-use.

Navigation

This page has all the information you need to get started with CopilotKit's headless UI. Select where you'd like to get started below.

Getting started

Build a fully headless chat interface from the ground up to get started.

Generative UI

Learn how to work with generative UI to render tools and other UI elements.

Suggestions

Learn how to work with suggestions to provide your users with a list of generated or static options to operate the chat with.

Human-in-the-loop

Learn how to work with human-in-the-loop to pause the chat and wait for a human to respond.

Reference

Reference documentation for the `useCopilotChatHeadless_c` hook.

Getting started

To get started, let's walk through building a simple chat interface. From there, we'll cover how to do more advanced things like working with suggestions and generative UI.

To get there, let's start by building a simple chat interface.

Create a new application

npx copilotkit@latest create

Set up your application

First, follow the instructions in the README to set up your application, it will be a simple process.

open README.md

Set up your CopilotKit provider

You will need to provide your public license key to the CopilotKit provider component. Get yours on Copilot Cloud or read more about premium features.

src/app/layout.tsx
<CopilotKit 
  publicLicenseKey="your-free-public-license-key"
>
  {children}
</CopilotKit>

Create a headless chat component

Replace your main page with a simple chat interface using the headless hook.

src/app/page.tsx
"use client";
import { useState } from "react";
import { useCopilotChatHeadless_c } from "@copilotkit/react-core"; 

export default function Home() {
  const { messages, sendMessage, isLoading } = useCopilotChatHeadless_c(); 
  const [input, setInput] = useState("");

  const handleSend = () => {
    if (input.trim()) {
      sendMessage({
        id: Date.now().toString(),
        role: "user",
        content: input,
      });
      setInput("");
    }
  };

  return (
    <div>
      <h1>My Headless Chat</h1>
      
      {/* Messages */}
      <div>
        {messages.map((message) => (
          <div key={message.id}>
            <strong>{message.role === "user" ? "You" : "Assistant"}:</strong>
            <p>{message.content}</p>
          </div>
        ))}

        {isLoading && <p>Assistant is typing...</p>}
      </div>

      {/* Input */}
      <div>
        <input 
          type="text" 
          value={input}
          onChange={(e) => setInput(e.target.value)}
          onKeyDown={(e) => e.key === "Enter" && handleSend()}
          placeholder="Type your message here..."
        />
        <button onClick={handleSend} disabled={isLoading}>
          Send
        </button>
      </div>
    </div>
  );
}

Working with Generative UI

You can also render generative UI either via useCopilotAction or by reading tools and rendering them directly.

With useCopilotAction

CopilotKit's standard components utilize useCopilotAction to allow tools to be both rendered and called in the frontend. These same interfaces are available to you while using the useCopilotChatHeadless_c hook.

src/app/components/chat.tsx
import { useCopilotAction } from "@copilotkit/react-core";

export const Chat = () => {
  // ...

  // Define an action that will show a custom component
  useCopilotAction({
    name: "showCustomComponent",
    // Handle the tool on the frontend
    handler: () => {
      return "Foo, Bar, Baz";
    },
    // Render a custom component for the underlying data
    render: ({ result, args, status}) => {
      return <div style={{
        backgroundColor: "red",
        padding: "10px",
        borderRadius: "5px",
      }}>
        <p>Custom component</p>
        <p>Result: {result}</p>
        <p>Args: {JSON.stringify(args)}</p>
        <p>Status: {status}</p>
      </div>;
    }
  });

  // ...

  return <div>
    {messages.map((message) => (
      <p key={message.id}>
        {message.role === "user" ? "User: " : "Assistant: "}
        {message.content}
        {/* Render the generative UI if it exists */}
        {message.role === "assistant" && message.generativeUI?.()}
      </p>
    ))}
  </div>
};

With raw data

If you don't want to use useCopilotAction, you can also render the raw data directly.

src/app/components/chat.tsx
export const Chat = () => {
  // ...

  return <div>
    {messages.map((message) => (
      <p key={message.id}>
        {/* Render the tool calls if they exist */}
        {message.role === "assistant" && message.toolCalls?.map((toolCall) => (
          <p key={toolCall.id}>
            {toolCall.function.name}: {toolCall.function.arguments}
          </p>
        ))}
      </p>
    ))}
  </div>
};

Working with suggestions

CopilotKit's suggestions are a way to provide your users with a list of suggestions to operate the chat with. When utilizing Headless UI, you have full control over the lifecycle of these suggestions.

Generating suggestions

You can generate suggestions by calling the generateSuggestions function.

src/app/components/chat.tsx
import { useCopilotChatHeadless_c, useCopilotChatSuggestions } from "@copilotkit/react-core"; 

export const Chat = () => {
  // Specify what suggestions should be generated
  useCopilotChatSuggestions({
    instructions:
      "Suggest 5 interesting activities for programmers to do on their next vacation",
    maxSuggestions: 5,
  });

  // Grab relevant state from the headless hook
  const { suggestions, generateSuggestions, sendMessage } = useCopilotChatHeadless_c(); 

  // Generate suggestions when the component mounts
  useEffect(() => {
    generateSuggestions(); 
  }, []);

  // ...

  return <div>
    {suggestions.map((suggestion, index) => (
      <button 
        key={index} 
        onClick={() => sendMessage({ 
          id: "123", 
          role: "user", 
          content: suggestion.message
        })}
      >
        {suggestion.title}
      </button>
    ))}
  </div>
};

Programmatically setting suggestions

If you want more deterministic control over the suggestions, you can set them manually.

src/app/components/chat.tsx
import { useCopilotChatHeadless_c } from "@copilotkit/react-core";

export const Chat = () => {
  // Grab relevant state from the headless hook
  const { suggestions, setSuggestions } = useCopilotChatHeadless_c();

  // Set the suggestions when the component mounts
  useEffect(() => {
    setSuggestions([
      { title: "Suggestion 1", message: "The actual message for suggestion 1" },
      { title: "Suggestion 2", message: "The actual message for suggestion 2" },
    ]);
  }, []);

  // Change the suggestions on function call
  const changeSuggestions = () => {
    setSuggestions([
      { title: "Foo", message: "Bar" },
      { title: "Baz", message: "Bat" },
    ]);
  };

  return (
    <div>
      {/* Change on button click */}
      <button onClick={changeSuggestions}>Change suggestions</button>

      {/* Render */}
      {suggestions.map((suggestion, index) => (
        <button 
          key={index} 
          onClick={() => sendMessage({ 
            id: "123", 
            role: "user", 
            content: suggestion.message
          })}
        >
          {suggestion.title}
        </button>
      ))}
    </div>
  );
};

Working with Human-in-the-loop

CopilotKit's human-in-the-loop (HITL) features allows you to pause the chat and wait for a human to respond. This comes in two flavors: tool-based and interrupt-based (for certain frameworks).

Tool-based

Tool-based HITL allows for you to pause completion of a tool call and wait for a human to respond. That human's response, becomes the result of the tool call.

src/app/components/chat.tsx
import { useCopilotAction, useCopilotChatHeadless_c } from "@copilotkit/react-core";

export const Chat = () => {
  const { messages, sendMessage } = useCopilotChatHeadless_c();

  // Define an action that will wait for the user to enter their name
  useCopilotAction({
    name: "getName",
    renderAndWaitForResponse: ({ respond, args, status}) => {
      if (status === "complete") {
        return <div>
          <p>Name retrieved...</p>
        </div>;
      }

      return <div>
        <input 
          type="text" 
          value={args.name || ""} 
          onChange={(e) => respond?.(e.target.value)} 
          placeholder="Enter your name" 
        />
        {/* Respond with the name */}
        <button onClick={() => respond?.(args.name)}>Submit</button>
      </div>;
    }
  });

  return (
    {messages.map((message) => (
      <p key={message.id}>
        {message.role === "user" ? "User: " : "Assistant: "}
        {message.content}
        {/* This will render the tool-based HITL if it exists */}
        {message.role === "assistant" && message.generativeUI?.()}
      </p>
    ))}
  )
};
PREV
CopilotKit Premium
Slanted end borderSlanted end border
Slanted start borderSlanted start border
NEXT
Observability

On this page

Overview
Navigation
Getting started
Working with Generative UI
With useCopilotAction
With raw data
Working with suggestions
Generating suggestions
Programmatically setting suggestions
Working with Human-in-the-loop
Tool-based