docs/snippets/shared/premium/headless-ui.mdx
import { ImageAndCode } from "@/components/react/image-and-code" import { MessageCircleDashed, Blocks, LayoutList, UserPen, BookOpen, } from "lucide-react"
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.
<OpsPlatformCTA variant="card" title="Headless UI requires an CopilotKit platform license key" body="Sign up for a free account to get a publicLicenseKey and unlock the headless hooks." ctaLabel="Create a free account" surface="docs_premium_headless_ui" />
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.
<Cards className="gap-6"> <Card icon={<MessageCircleDashed className="text-primary" />} className="p-6 rounded-xl text-base md:col-span-2" title="Getting started" description="Build a fully headless chat interface from the ground up to get started." href="#getting-started" /> <Card icon={<Blocks className="text-primary" />} className="p-6 rounded-xl text-base" title="Generative UI" description="Learn how to work with generative UI to render tools and other UI elements." href="#working-with-generative-ui" /> <Card icon={<LayoutList className="text-primary" />} className="p-6 rounded-xl text-base" title="Suggestions" description="Learn how to work with suggestions to provide your users with a list of generated or static options to operate the chat with." href="#working-with-suggestions" /> <Card icon={<UserPen className="text-primary" />} className="p-6 rounded-xl text-base" title="Human-in-the-loop" description="Learn how to work with human-in-the-loop to pause the chat and wait for a human to respond." href="#working-with-human-in-the-loop" /> <Card icon={<BookOpen className="text-primary" />} className="p-6 rounded-xl text-base" title="Reference" description="Reference documentation for the `useCopilotChatHeadless_c` hook." href="/reference/v1/hooks/useCopilotChatHeadless_c" /> </Cards>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.
<video src="https://cdn.copilotkit.ai/docs/copilotkit/videos/full-headless-chat.mp4" className="rounded-xl shadow-lg border" loop playsInline autoPlay muted />
To get there, let's start by building a simple chat interface.
<Steps> <Step> ### Create a new application ```bash npx copilotkit@latest create ``` </Step> <Step> ### Set up your application First, follow the instructions in the README to set up your application, it will be a simple process.```bash
open README.md
```
```tsx title="src/app/layout.tsx"
<CopilotKit
publicLicenseKey="your-free-public-license-key"
>
{children}
</CopilotKit>
```
```tsx title="src/app/page.tsx"
"use client";
import { useState } from "react";
import { useCopilotChatHeadless_c } from "@copilotkit/react-core/v2"; // [!code highlight]
export default function Home() {
const { messages, sendMessage, isLoading } = useCopilotChatHeadless_c(); // [!code highlight]
const [input, setInput] = useState("");
const handleSend = () => {
if (input.trim()) {
// [!code highlight:5]
sendMessage({
id: Date.now().toString(),
role: "user",
content: input,
});
setInput("");
}
};
return (
<div>
<h1>My Headless Chat</h1>
<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>
<div>
<input
type="text"
value={input}
onChange={(e) => setInput(e.target.value)}
// [!code highlight:1]
onKeyDown={(e) => e.key === "Enter" && handleSend()}
placeholder="Type your message here..."
/>
<button onClick={handleSend} disabled={isLoading}>
Send
</button>
</div>
</div>
);
}
```
You can also render generative UI either via useCopilotAction or by reading tools and rendering them directly.
<video src="https://cdn.copilotkit.ai/docs/copilotkit/videos/gen-ui.mp4" className="rounded-xl shadow-lg border" loop playsInline autoPlay muted />
useCopilotActionCopilotKit'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.
import { useFrontendTool } from "@copilotkit/react-core/v2";
export const Chat = () => {
// ...
// Define an action that will show a custom component
useFrontendTool({
name: "showCustomComponent",
// Handle the tool on the frontend
// [!code highlight:3]
handler: () => {
return "Foo, Bar, Baz";
},
// Render a custom component for the underlying data
// [!code highlight:13]
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}
{message.role === "assistant" && message.generativeUI?.()}
</p>
))}
</div>
};
If you don't want to use useCopilotAction, you can also render the raw data directly.
export const Chat = () => {
// ...
return <div>
{messages.map((message) => (
<p key={message.id}>
{message.role === "assistant" && message.toolCalls?.map((toolCall) => (
<p key={toolCall.id}>
{toolCall.function.name}: {toolCall.function.arguments}
</p>
))}
</p>
))}
</div>
};
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.
<video src="https://cdn.copilotkit.ai/docs/copilotkit/videos/suggestions.mp4" className="rounded-xl shadow-lg border" loop playsInline autoPlay muted />
You can generate suggestions by calling the generateSuggestions function.
import { useCopilotChatHeadless_c, useCopilotChatSuggestions } from "@copilotkit/react-core/v2"; // [!code highlight]
export const Chat = () => {
// Specify what suggestions should be generated
// [!code highlight:5]
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(); // [!code highlight]
// Generate suggestions when the component mounts
useEffect(() => {
generateSuggestions(); // [!code highlight]
}, []);
// ...
// [!code word:suggestion]
return <div>
{suggestions.map((suggestion, index) => (
<button
key={index}
onClick={() => sendMessage({
id: "123",
role: "user",
content: suggestion.message
})}
>
{suggestion.title}
</button>
))}
</div>
};
If you want more deterministic control over the suggestions, you can set them manually.
import { useCopilotChatHeadless_c } from "@copilotkit/react-core/v2";
export const Chat = () => {
// Grab relevant state from the headless hook
// [!code highlight:1]
const { suggestions, setSuggestions } = useCopilotChatHeadless_c();
// Set the suggestions when the component mounts
// [!code highlight:6]
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 = () => {
// [!code highlight:4]
setSuggestions([
{ title: "Foo", message: "Bar" },
{ title: "Baz", message: "Bat" },
]);
};
// [!code word:suggestion]
return (
<div>
<button onClick={changeSuggestions}>Change suggestions</button>
{suggestions.map((suggestion, index) => (
<button
key={index}
onClick={() => sendMessage({
id: "123",
role: "user",
content: suggestion.message
})}
>
{suggestion.title}
</button>
))}
</div>
);
};
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).
<video src="https://cdn.copilotkit.ai/docs/copilotkit/videos/hitl.mp4" className="rounded-xl shadow-lg border" loop playsInline autoPlay muted />
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.
import { useFrontendTool, useCopilotChatHeadless_c } from "@copilotkit/react-core/v2";
export const Chat = () => {
const { messages, sendMessage } = useCopilotChatHeadless_c();
// Define an action that will wait for the user to enter their name
useFrontendTool({
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"
/>
<button onClick={() => respond?.(args.name)}>Submit</button>
</div>;
}
});
return (
{messages.map((message) => (
<p key={message.id}>
{message.role === "user" ? "User: " : "Assistant: "}
{message.content}
{message.role === "assistant" && message.generativeUI?.()}
</p>
))}
)
};