Integrate Copilot feature into your React applications using CopilotKit
Tags: copilotkit, webdev, chatgpt
Published: 2024-05-03 11:45:52

### 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](https://github.com/features/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:

```jsx
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.

{% embed https://vimeo.com/942293741?share=copy %}

Tech stack of the project:
- NextJS
- Typescript
- [Shadcn UI](https://ui.shadcn.com/)
- State Management: [Zustand](https://github.com/pmndrs/zustand)
- [Reactflow](https://reactflow.dev/): for drawing mindmap

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](https://nextjs.org/docs/app) for more information.

Add the file `src/app/api/copilotkit/openai/route.ts`

```jsx
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.

```jsx
OPENAI_API_KEY=sk-<your-api-key>
```

You can get your key [here](https://platform.openai.com/account/api-keys).

### 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. 

```jsx
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`

```jsx
useMakeCopilotReadable(JSON.stringify(nodes));
```

You can also use **`useMakeCopilotDocumentReadable`** hook for another document type like Google Docs. Read the docs [here](https://docs.copilotkit.ai/getting-started/quickstart-chatbot).

Demo:

{% embed https://vimeo.com/942293709?share=copy %}


### 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`

```jsx
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:

{% embed https://vimeo.com/942293727?share=copy %}


### 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` 

```jsx
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.

```jsx
<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.

```jsx
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:

{% embed https://vimeo.com/942293687?share=copy %}

## 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](https://docs.copilotkit.ai/reference/CopilotTask):** This class is used to execute one-off tasks, for example on button press. It can use the context available via **[useCopilotReadable](https://docs.copilotkit.ai/useCopilotReadable)** and the actions provided by **[useCopilotAction](https://docs.copilotkit.ai/useCopilotAction)**, or you can provided your own context and actions.
- **[In-App Agents (via LangChain and LangGraph)](https://docs.copilotkit.ai/getting-started/quickstart-skills-langchain):** 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](https://copilotkit.ai/) 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 :)