How to build: a v0.dev clone (Next.js, GPT4 & CopilotKit)
Tags: webdev, tutorial, javascript, programming
Published: 2024-04-04 09:54:07

## TL;DR
In this article, you will learn how to build a clone of Vercel's V0.dev. This is an awesome project to add to your portfolio and to hone in your AI chops. 

We will cover using: 
- Next.js for the app framework 🖥️
- OpenAI for the LLM 🧠
- App logic of v0 👾
- Using CopilotKit to integrate the AI into your app 🪁


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/shhjdu2k5s02gzoby3p0.gif)

---

# CopilotKit: the OS framework for in-app AI

CopilotKit is the [open-source AI copilot platform.](https://github.com/CopilotKit/CopilotKit) We make it easy to integrate powerful AI into your react apps. 

Build:
- ChatBot: Context aware in-app chatbots that can take actions in-app 💬

- CopilotTextArea: AI-powered textFields with context-aware autocomplete & insertions 📝

- Co-Agents: In-app AI agents that can interact with your app & users 🤖



![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i8gltoave8490fg234ro.gif)

{% cta https://github.com/CopilotKit/CopilotKit %} Star CopilotKit ⭐️ {% endcta %}
(forgive the AI's spelling mistake & give a star :)



Now back to the article. 

---

## Prerequisites
To get started with this tutorial, you need the following:
- A text editor (VS Code, Cursor)
- Basic knowledge of React, Next.js, Typescript, and Tailwind CSS.
- Node.js installed on your PC/Mac
- A package manager (npm)
- [OpenAI](https://platform.openai.com/docs/overview) API key
- [CopilotKit](https://docs.copilotkit.ai/getting-started/quickstart-textarea) installed in your React project

## What is v0?
**v0** is a Generative user interface (UI) tool [developed by Vercel](https://vercel.com/blog/announcing-v0-generative-ui) that allows users to give prompts and describe their ideas which are translated into UI code for creating web interfaces. It utilizes [generative AI](https://medium.com/data-science-at-microsoft/generative-ai-openai-and-chatgpt-what-are-they-3c80397062c4), along with open-source tools such as [React](https://developer.mozilla.org/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_getting_started), [Tailwind CSS](https://tailwindcss.com/), and [Shadcn UI](https://ui.shadcn.com/), to produce code based on descriptions provided by the user.

_Here is an example of a web app UI generated with v0_
https://v0.dev/t/nxGnMd1uVGc

## Understanding the Project Requirements
At the end of this step-by-step tutorial, the clone will have these project requirements:
1. **User Input:** Users input text as prompts describing the UI they want to generate. This will be done using the CopilotKit chatbot, made available by the [CopilotSidebar](https://docs.copilotkit.ai/reference/CopilotSidebar).
2. **CopilotKit Integration:** CopilotKit will be used to give AI functionality to the web app for generating UIs.
3. **Rendering the UI:** A toggle to switch between the UI React/JSX code and rendered UI.

## Creating the v0 clone with CopilotKit
**Step 1: Create a new Next.JS app**
Open your workspace folder in your terminal and run the following command create a new Next.js app:
```shell
npx create-next-app@latest copilotkit-v0-clone
```

This will create a new directory named `copilotkit-v0-clone`  with a Next.JS project structure, with the required dependencies installed. It will show this in your terminal, choose **Yes** for all of them except the last one, as the default `import alias` is recommended. The other prompts install Typescript and TailwindCSS which we will use in the project.

![terminal](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o6l07dxihdi8hw68kv4e.png)

Navigate to the project directory using the `cd` command like so:

```shell
cd copilotkit-v0-clone
```

**Step 2: Setup CopilotKit Backend Endpoint. Read the [docs](https://docs.copilotkit.ai/getting-started/quickstart-backend) to learn more.**

Run this command to install the CopilotKit backend packages:
```shell
npm i @copilotkit/backend
```
Then visit https://platform.openai.com/api-keys to get your **GPT 4** OpenAI API key.

![openai](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ise7y3qnb3cyg4j0mjr6.png)

Once you have your API key, create a `.env.local` file in the root directory.
The `.env.local` file should be like this:
```shell
OPENAI_API_KEY=Your OpenAI API key
```
In the **app** directory create this directory; `api/copilot/openai` and create a file named `route.ts` This file serves as the **backend** endpoint for CopilotKit requests and OpenAI interactions. It handles incoming requests, processes them using CopilotKit, and returns the appropriate response.

We will create a POST request function in the `route.ts` file, inside the post request create a new instance of `CopilotBackend` class, this class provides methods for processing CopilotKit requests.
We then call the `response` method of the `CopilotBackend` instance, passing the request object (`req`) and a new instance of the `OpenAIAdapter` class as arguments. This method processes the request using CopilotKit and the OpenAI API and returns a response.

As shown in the code below, we import the `CopilotBackend` and `OpenAIAdapter` classes from the `@copilotkit/backend` package. These classes are necessary for interacting with CopilotKit and the OpenAI API.

```js
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());
}
```

**Step 3: Creating Components for the v0 clone**
We will be using components from Shadcn UI library. To process let’s setup Shadcn UI library by running the `shadcn-ui init` command to setup your project

```shell
npx shadcn-ui@latest init
```
Then we will configure the components.json with this questions
```shell
Which style would you like to use? › Default
Which color would you like to use as base color? › Slate
Do you want to use CSS variables for colors? › no / yes
```
The components we are using from Shadcn UI are **button** and **dialog**. So let’s install them!
For the [button](https://ui.shadcn.com/docs/components/button), run this command
```shell
npx shadcn-ui@latest add button
```
To install the [dialog](https://ui.shadcn.com/docs/components/dialog) component run the command below
```shell
npx shadcn-ui@latest add dialog
```

**Step 4: Setup CopilotKit Frontend. Read the [docs](https://docs.copilotkit.ai/getting-started/quickstart-textarea) to learn more.**
To install the CopilotKit frontend packages run this command:
```shell
npm i @copilotkit/react-core @copilotkit/react-ui 
```

From the [CopilotKit documentation](https://docs.copilotkit.ai/getting-started/quickstart-textarea), to use CopilotKit, we must set up the frontend wrapper to pass any React app through Copilot. When the prompt is passed to CopilotKit, it sends it through the URL to OpenAI which returns the response.

In the **app** directory, let’s update the `layout.tsx` file. This file will define the layout structure of our application and integrate CopilotKit into the frontend.

Enter the code below:

```js
"use client";
import { CopilotKit } from "@copilotkit/react-core";
import "@copilotkit/react-textarea/styles.css"; // also import this if you want to use the CopilotTextarea component
import "@copilotkit/react-ui/styles.css"; 
import { Inter } from "next/font/google";
import "./globals.css";
import { CopilotSidebar,  } from "@copilotkit/react-ui";

const inter = Inter({ subsets: ["latin"] });

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html lang="en">
      <body className={inter.className}>
        <CopilotKit url="/api/copilotkit/openai/">
          <CopilotSidebar defaultOpen>{children}</CopilotSidebar>
        </CopilotKit>
      </body>
    </html>
  );
}
```
This component represents the root layout of our application. It wraps the entire application with CopilotKit, specifying the URL for CopilotKit's backend endpoint (`/api/copilotkit/openai/`), based on what we created in the **Step 2** for the backend. Additionally, it includes a CopilotSidebar component, acting as a sidebar for CopilotKit, with the children prop passed as its content.

**Step 5: Setting up the Main App**

Let’s create the structure of the application. It will have a Header, Sidebar and Preview screen.

For the **Header**, navigate to the **components** directory like this, `src/components` then create a `header.tsx` file and enter the code below:

```js
import { CodeXmlIcon } from "lucide-react";
import { Button } from "./ui/button";

const Header = (props: { openCode: () => void }) => {
    return (
      <div className="w-full h-20 bg-white flex justify-between items-center px-4">
        <h1 className="text-xl font-bold">Copilot Kit</h1>
        <div className="flex gap-x-2">
          <Button
            className="  px-6 py-1 rounded-md space-x-1"
            variant={"default"}
            onClick={props.openCode}
          >
            <span>Code</span> <CodeXmlIcon size={20} />
          </Button>
        </div>
      </div>
    );
  };

  export default Header;
```

For **Sidebar** create a `sidebar.tsx` file and enter this code:

```js
import { ReactNode } from "react";

const Sidebar = ({ children }: { children: ReactNode }) => {
  return (
    <div className="w-[12%] min-h-full bg-white rounded-md p-4">
      <h1 className="text-sm mb-1">History</h1>
      {children}
    </div>
  );
};

export default Sidebar;

```

Then for the **Preview** screen, create a `preview-screen.tsx` file and enter the code:
```js
const PreviewScreen = ({ html_code }: { html_code: string }) => {
    return (
      <div className="w-full h-full bg-white rounded-lg  shadow-lg p-2 border">
        <div dangerouslySetInnerHTML={{ __html: html_code }} />
      </div>
    );
  };
  export default PreviewScreen; 
```

Now let’s bring them together, open the `page.tsx` file and paste the following code:

```js
"use client";
import { useState } from "react";
import {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogHeader,
  DialogTitle,
} from "@/components/ui/dialog";
import Header from "@/components/header";
import Sidebar from "@/components/sidebar";
import PreviewScreen from "@/components/preview-screen";
import { Input } from "@/components/ui/input";

export default function Home() {
  const [code, setCode] = useState<string[]>([
    `<h1 class="text-red-500">Hello World</h1>`,
  ]);
  const [codeToDisplay, setCodeToDisplay] = useState<string>(code[0] || "");
  const [showDialog, setShowDialog] = useState<boolean>(false);
  const [codeCommand, setCodeCommand] = useState<string>("");
  
  return (
    <>
      <main className="bg-white min-h-screen px-4">
        <Header openCode={() => setShowDialog(true)} />
        <div className="w-full h-full min-h-[70vh] flex justify-between gap-x-1 ">
          <Sidebar>
            <div className="space-y-2">
              {code.map((c, i) => (
                <div
                  key={i}
                  className="w-full h-20 p-1 rounded-md bg-white border border-blue-600"
                  onClick={() => setCodeToDisplay(c)}
                >
                  v{i}
                </div>
              ))}
            </div>
          </Sidebar>

          <div className="w-10/12">
            <PreviewScreen html_code={codeToDisplay || ""} />
          </div>
        </div>
        <div className="w-8/12 mx-auto p-1 rounded-full bg-primary flex my-4 outline-0">
          <Input
            type="text"
            placeholder="Enter your code command"
            className="w-10/12 p-6 rounded-l-full  outline-0 bg-primary text-white"
            value={codeCommand}
            onChange={(e) => setCodeCommand(e.target.value)}
          />
          <button
            className="w-2/12 bg-white text-primary rounded-r-full"
            onClick={() => generateCode.run(context)}
          >
            Generate
          </button>
        </div>
      </main>
      <Dialog open={showDialog} onOpenChange={setShowDialog}>
        <DialogContent>
          <DialogHeader>
            <DialogTitle>View Code.</DialogTitle>
            <DialogDescription>
              You can use the following code to start integrating into your
              application.
            </DialogDescription>
            <div className="p-4 rounded bg-primary text-white my-2">
              {readableCode}
            </div>
          </DialogHeader>
        </DialogContent>
      </Dialog>
    </>
  );
}
```
Let’s breakdown the code above:

`const [code, setCode] = useState<string[]>([]);` will  be used to hold the generated code

`const [codeToDisplay, setCodeToDisplay] = useState<string>(code[0] || "");` will be used to hold the code that is displayed on the Preview Screen.

`const [showDialog, setShowDialog] = useState<boolean>(false);` this will hold the state of the dialog box that shows the generated code you can copy.

In the code below, we loop over the generated code, which is a string of arrays, to show it on the Sidebar, so that when we select one, it is displayed on the Preview screen.
```js
 <Sidebar>
            <div className="space-y-2">
              {code.map((c, i) => (
                <div
                  key={i}
                  className="w-full h-20 p-1 rounded-md bg-white border border-blue-600"
                  onClick={() => setCodeToDisplay(c)}
                >
                  v{i}
                </div>
              ))}
            </div>
          </Sidebar>
```

`<PreviewScreen html_code={codeToDisplay} />` here, we send the code to be displayed on the preview screen. The preview screen component takes the string of code generated by CopilotKit and uses `dangerouslySetInnerHTML` to render the generated code.

Below we have a `Dialog` component that will display the code generated by CoplilotKit that can be copied and added to your code.
```js
<Dialog open={showDialog} onOpenChange={setShowDialog}>
        <DialogContent>
          <DialogHeader>
            <DialogTitle>View Code.</DialogTitle>
            <DialogDescription>
              You can use the following code to start integrating into your
              application.
            </DialogDescription>
            <div className="p-4 rounded bg-primary text-white my-2">
              {readableCode}
            </div>
          </DialogHeader>
        </DialogContent>
      </Dialog>
```

**Step 6: Implementing the Main Application Logic**
For this step, we'll integrate CopilotKit into our v0 clone application to facilitate AI-powered UI generation. We'll use CopilotKit's React hooks to manage state, make components readable and actionable by Copilot, and interact with the OpenAI API.

On your `page.tsx` file, import this:
```js
import {
  CopilotTask,
  
  useCopilotContext,
  useMakeCopilotReadable,
} from "@copilotkit/react-core";
```
Then we define a `generateCode` task using `CopilotTask` in the `Home` component:

```js

 const readableCode = useMakeCopilotReadable(codeToDisplay);
 
 const generateCode = new CopilotTask({
    instructions: codeCommand,
    actions: [
      {
        name: "generateCode",
        description: "Create Code Snippet with React.js, tailwindcss.",
        parameters: [
          {
            name: "code",
            type: "string",
            description: "Code to be generated",
            required: true,
          },
        ],
        handler: async ({ code }) => {
          setCode((prev) => [...prev, code]);
          setCodeToDisplay(code);
        },
      },
    ],
  });
  
  const context = useCopilotContext();
```

We use `useMakeCopilotReadable`, to pass the existing code and ensure readability. Then we use `CopilotTask` to generate the UI and bind the `generateCode` task to the **generate** button, which enables the generation of code snippets by interacting with the button component.
This action is triggered by user interactions and executes an asynchronous `handler` function when invoked.

The `handler` adds the code generated to the code array, updates the application state to include the newly generated code snippet and also sends the generated code to be displayed and rendered on the Preview screen, which is available to be copied too. 
Also, the `instructions` attribute specifies the command provided to Copilot, which is stored in the `codeCommand` state variable.

For a full description of how `CopilotTask` works, check the documentation here: https://docs.copilotkit.ai/reference/CopilotTask

**Step 7: Run the v0 Clone Application**

At this point, we have completed the v0 clone setup and can then start the development server by running
```shell
npm run dev
```


![terminal](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6wm5oddqlzewca34ko0g.png)




The web app can be accessed in your browser with this URL

[http://localhost:3000](http://localhost:3000/)

You can then enter a prompt and click on **Generate.** Here are some examples:

- **Pricing page:** As shown below, here is the generated UI, with a toggle button to switch between the UI and React code:

![vo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cm3gyodvbl0x9i0uvxp9.png)

If you click on the **Code** </> button on the top right, it switches to the React code for the UI generated like so:


![vo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1b5sruonnxl7x42ad8y1.png)

- A Signup page UI example:


![signup](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/350l7o66l6lq5d4kxiav.png)


- And also a Checkout page

![checkout](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dpj6j338fp2gavsvgtti.png)




To clone the project and run it locally, open your terminal and run this command:

```shell
git clone https://github.com/Tabintel/v0-copilot-next
```
Then run `npm install` to install all dependencies needed for the project and `npm run dev` to run the web app.


## Conclusion
In conclusion, you can use [CopilotKit](https://github.com/CopilotKit/CopilotKit) to build a v0 clone to give UI prompts for your design. CopilotKit goes beyond UI prompts, it can also be used in building applications like [AI-powered PowerPoint generators](https://dev.to/copilotkit/how-to-build-ai-powered-powerpoint-app-nextjs-openai-copilotkit-ji2), [AI resume builders](https://dev.to/copilotkit/how-to-build-the-with-nextjs-openai-1mhb), and many more.
The possibilities are endless, check out CopilotKit today and bring your AI ideas to life.
Get the full source code on [GitHub](https://github.com/Tabintel/v0-copilot-next).
Learn more about how to use CopilotKit from the [documentation](https://docs.copilotkit.ai/getting-started/quickstart-textarea).

Also, don't forget to [Star CopilotKit!](https://github.com/CopilotKit/CopilotKit) ⭐