Introduction
In the ever-evolving landscape of web development, enhancing user experience is necessary. One innovative way to achieve this is by integrating an AI copilot into your application. Inspired by tools like GitHub Copilot, which assists developers by suggesting code snippets and entire functions in real-time, AI copilots can dramatically streamline user interactions within an app.
What is an AI Copilot?
An AI copilot acts as an intelligent assistant within your application, guiding the user through tasks, offering suggestions, and even performing actions autonomously. They leverage AI technologies to understand and anticipate needs based on user input and context.
Introducing CopilotKit
CopilotKit is an open-source AI platform designed specifically for React applications. It equips developers with the tools needed to seamlessly integrate powerful AI functionalities into their apps.
Capabilities of CopilotKit
CopilotKit allows developers to:
- Embed a contextual chat feature: Engage users directly by answering queries and performing tasks based on conversational inputs.
- Enable action-driven assistance: Allow the copilot to execute specific actions within the web environment, enhancing interactivity and efficiency.
- Offer dynamic content recommendations: By analyzing user inputs, CopilotKit can suggest relevant content, significantly improving the user experience by catering to individual needs and preferences.
By integrate CopilotKit into your React project, you not only make your application smarter but also significantly enhance the overall user interaction, making it more engaging and responsive.
Let’s dive into how you can integrate CopilotKit and transform your React application into a more intelligent platform.
Prerequisites
To fully understand this tutorial, you need to have a basic understanding of React or Next.js.
You also need to create OpenAI API key to use ChatGPT models. Currently, CopilotKit only supports OpenAI API. I hope they will provide more models in the future to provide developers with more flexibility and choice.
Set up and Package Installation
First, we need to install these dependencies into the project
-
@copilotkit/react-core
: CopilotKit frontend package with react hooks for providing app-state and actions to the copilot (AI functionalities) -
@copilotkit/react-ui
: CopilotKit frontend package for the chatbot sidebar UI -
@copilotkit/react-textarea
: CopilotKit frontend package for AI-assisted text-editing in the presentation speaker notes. -
@copilotkit/backend
: CopilotKit backend package
Install these packages:
npm i @copilotkit/react-core @copilotkit/react-ui @copilotkit/react-textarea @copilotkit/backend
Example: Enhancing a Mindmap Builder with CopilotKit
About the example project
The application we will focus on is a Mindmap builder. This tool enables users to create their mindmaps, allowing them to add and manage content within each node. By integrating an AI copilot, we aim to enrich the user experience by streamlining interactions and providing intelligent content suggestions.
Here is the demo of the application’s main flow.
Tech stack of the project:
If you want to know more about this tech stack, you can check out my template project: https://github.com/ngviethoang/next-shadcn-template
Because of the limitations of this post, I will leave the implementation of this application to another post. Let me know if you are interested.
You can check the project source code here: https://github.com/ngviethoang/MindmapCopilot
In the upcoming sections, I will guide you through the integration of the main features of CopilotKit into the project. This will include how to embed a contextual chat feature and facilitate action-driven assistance within the Mindmap application.
Copilot chatbot sidebar
Setup backend
This project uses NextJS App Router, you can check here for more information.
Add the file src/app/api/copilotkit/openai/route.ts
import { CopilotBackend, OpenAIAdapter } from '@copilotkit/backend';
export const runtime = 'edge';
export async function POST(req: Request): Promise<Response> {
const copilotKit = new CopilotBackend();
return copilotKit.response(req, new OpenAIAdapter({ model: 'gpt-4-turbo' }));
}
Here I am using gpt-4-turbo
, the latest model, you can also change this to another model.
To use ChatGPT models, of course we need to add OpenAI API key. Let’s create .env
file and enter your key.
OPENAI_API_KEY=sk-<your-api-key>
You can get your key here.
Setup frontend
First, let’s wrap CopilotKit
component outside in the layout component in file src/app/layout.tsx
The url
props here in CopilotKit
is the backend URL that we set up above.
To use the chatbot sidebar component, let’s add CopilotSidebar
component into the page you want. Here I add it directly to the layout component.
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body className={inter.className}>
<ThemeProvider
attribute="class"
defaultTheme="system"
enableSystem
disableTransitionOnChange={false}
>
<CopilotKit url="/api/copilotkit/openai">
<CopilotSidebar
labels={{
initial:
'Welcome to the Mindmap Copilot app! How can I help you?',
}}
>
{children}
</CopilotSidebar>
</CopilotKit>
<Analytics />
</ThemeProvider>
</body>
</html>
);
}
The initial label is the first message that the chatbot use to welcome user. You can change it as you like.
Provide context to chatbot
To provide more information about the application for the chatbot, we use the useMakeCopilotReadable
hook.
useCopilotReadable
is a React hook that provides app-state and other information to the Copilot. Optionally, the hook can also handle hierarchical state within your application, passing these parent-child relationships to the Copilot.
In this project I want provide the chatbot with the context of the current nodes of the mindmap that user is creating. So the chatbot can have enough information to answer user and perform actions based on it.
Let’s add this hook into the mindmap component in file src/components/mindmap/index.tsx
useMakeCopilotReadable(JSON.stringify(nodes));
You can also use useMakeCopilotDocumentReadable
hook for another document type like Google Docs. Read the docs here.
Demo:
Copilot action
useCopilotAction
is a React hook that lets you integrate actionable functions in the Copilot chat. The Copilot can then call these actions to trigger actions in your application, allowing for interactive and dynamic behavior controlled by the Copilot.
Let’s take an example of an action in this app: the copilot will add the nodes that user asks it automatically in any nodes.
Let’s add this hook into the mindmap component in file src/components/mindmap/index.tsx
useCopilotAction({
name: 'addNodes', // The name of the action
description: 'Add some nodes to the mindmap', // A description of the action. This is used to instruct the Copilot on how to use the action.
parameters: [
{
name: 'nodes', // The name of the parameter
type: 'object[]', // The type of the argument.
description: 'The nodes to add', // A description of the argument. This is used to instruct the Copilot on what this argument is used for.
attributes: [ // define the attributes of the complex object
{
name: 'label',
type: 'string',
description: 'The name of the node',
},
{
name: 'parentId',
type: 'string',
description: 'The id of the parent node based on provided nodes',
},
],
},
],
handler: (props) => { // The handler of the action
const { nodes } = props;
addNodesFromCopilot(nodes);
},
});
For handler
function, we will pass the nodes
props from ChatGPT response to a predefined function. The function addNodesFromCopilot
is an Zustand action that add new nodes to the exact position in the mindmap.
You can use this method to add more actions in your app by letting the copilot to decide the props and when to call the funtion.
Demo:
Copilot textarea
Lastly, we want the copilot to add recommendation content to the textarea. To do this, we can use the CopilotTextarea
component from the CopilotKit.
In this project, I created a custom component so it can be reused in multiple pages.
Check this file src/components/common/custom-copilot-textarea.tsx
import { CopilotTextarea } from '@copilotkit/react-textarea';
import { useRef, useState } from 'react';
import { cn } from '@/lib/utils';
interface CustomCopilotTextareaProps {
placeholder: string;
textareaPurpose: string;
maxTokens?: number;
defaultValue?: string;
debounceDuration?: number;
onDebouncedUpdate?: (value: string) => void;
className?: string;
}
export function CustomCopilotTextarea({
placeholder,
textareaPurpose,
maxTokens,
defaultValue,
debounceDuration,
onDebouncedUpdate,
className,
}: CustomCopilotTextareaProps) {
const [text, setText] = useState(defaultValue || '');
// debounce the update
let timeout = useRef<any>(null);
const debouncedUpdate = (value: string) => {
timeout.current && clearTimeout(timeout.current);
timeout.current = setTimeout(() => {
onDebouncedUpdate?.(value);
}, debounceDuration || 750);
};
// update the value
const handleValueChange = (value: string) => {
setText(value);
debouncedUpdate(value);
};
return (
<CopilotTextarea
className={cn('w-full h-full p-4 overflow-hidden', className)}
value={text}
onValueChange={handleValueChange}
placeholder={placeholder}
autosuggestionsConfig={{ // ChatGPT config
textareaPurpose,
chatApiConfigs: {
suggestionsApiConfig: {
forwardedParams: {
max_tokens: maxTokens || 50,
stop: ['.', '?', '!'],
},
},
},
}}
/>
);
}
In file src/components/mindmap/node-detail/index.tsx
, we add this component inside.
<CustomCopilotTextarea
placeholder="Enter content here..."
textareaPurpose={getNodeContentPurpose}
defaultValue={selectedNode.data.content || ''}
onDebouncedUpdate={(val) => {
setData(
nodes.map((node) => {
if (node.id === selectedNode.id) {
return {
...node,
data: {
...node.data,
content: val,
},
};
}
return node;
}),
edges
);
}}
/>
We provide the context to this textarea component by this function.
const getNodeContentPurpose = useMemo(() => {
if (!selectedNode) {
return '';
}
const parents = [];
let curNode = selectedNode;
while (curNode) {
const parent = nodes.find((n) => n.id === curNode.parentNode);
if (!parent) {
break;
}
parents.push(parent);
curNode = parent;
}
const parentsString = parents.map((node) => node.data.label).join(', ');
return `Content of the node: ${selectedNode?.data.label} (${parentsString})`;
}, [selectedNode, nodes]);
Demo:
Additional features from CopilotKit
Some features are not included in this example project, however, I recommend you give it a try and see if it fits your needs.
- CopilotTask: This class is used to execute one-off tasks, for example on button press. It can use the context available via useCopilotReadable and the actions provided by useCopilotAction, or you can provided your own context and actions.
- In-App Agents (via LangChain and LangGraph): CopilotKit will route relevant state as input to the standalone skill chains (state can come from the frontend, backend, 3rd party integrations, or from the user). When the chain returns, the Copilot Engine funnels its output to in-app interaction as needed.
Conclusion
CopilotKit is an incredible tool that allows you to add AI Copilots to your products within minutes. Whether you're interested in AI chatbots and assistants or automating complex tasks, CopilotKit makes it easy.
You can find the source code for this tutorial on GitHub: https://github.com/ngviethoang/MindmapCopilot
Thank you for reading :)
Top comments (1)
The CopilotKit workflow is well explained - thanks for sharing this tutorial! ⚔️