Build an AI-powered blogging platform (Next.js, Langchain & CopilotKit)
Tags: webdev, tutorial, programming, javascript
Published: 2024-04-11 14:18:43

## **TL;DR**

In this article, you will learn how to build an AI-powered blogging platform that can search the web and research any topic for a blog article. 

We will be covering:

- Next.js for the app framework 🖥️
- OpenAI for the LLM 🧠
- LangChain & Tavily for a web-searching AI agent 🤖
- Using CopilotKit to integrate the AI into your app 🪁
- Supabase for storing and retrieving the blogging platform article data.


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


---
# CopilotKit: the open-source Copilot framework

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

Build:
- ChatBots💬: 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. Powered by LangChain.



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

{% cta https://github.com/CopilotKit/CopilotKit %} Star CopilotKit ⭐️ {% endcta %}


Now back to the article. 

---

## **Prerequisites**

Before we start building the app, let us first see the dependencies or packages we need to build the app

- `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.
- `LangChainJS:` A framework for developing applications powered by language models.
- `Tavily Search API:` An API for helping connect LLMs and AI applications to trusted and real-time knowledge.

## Installing All The Project Packages and Dependencies

Before installing all the project packages and dependencies, let us first create a Nextjs project by running the following command on your terminal.

```jsx
npx create-next-app@latest
```

Then you will be prompted to select some options. Feel free to mark them, as shown below.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1j04grq2jykx6o606u1s.png)

After that, open the newly created Nextjs project using a text editor of your choice. Then run the command below on the command line to install all the project packages and dependencies.

```jsx
npm i @copilotkit/backend @copilotkit/shared @langchain/langgraph @copilotkit/react-core @copilotkit/react-ui @copilotkit/react-textarea @supabase/ssr @supabase/auth-helpers-nextjs
```

## **Creating The Blogging Platform Frontend**

In this section, I will walk you through the process of creating the frontend of the blogging platform with static content to define the platform’s user interface. 

To get started, go to `/[root]/src/app` and create a folder called `components`. Inside the components folder, create a file named `Article.tsx`.

After that, add the following code to the file that defines a functional component named `Article` that will be used to render the article creation form. 

```tsx
"use client";

import { useRef, useState } from "react";

export function Article() {
  // Define state variables for article outline, copilot text, and article title
  const [articleOutline, setArticleOutline] = useState("");
  const [copilotText, setCopilotText] = useState("");
  const [articleTitle, setArticleTitle] = useState("");

  return (
    // Form element for article input
    <form
      action={""}
      className="w-full h-full gap-10 flex flex-col items-center p-10">
      {/* Input field for article title */}
      <div className="flex w-full items-start gap-3">
        <textarea
          className="p-2 w-full h-12 rounded-lg flex-grow overflow-x-auto overflow-y-hidden whitespace-nowrap"
          id="title"
          name="title"
          value={articleTitle}
          placeholder="Article Title"
          onChange={(event) => setArticleTitle(event.target.value)}
        />
      </div>

      {/* Textarea for article content */}
      <textarea
        className="p-4 w-full aspect-square font-bold text-xl bg-slate-800 text-white rounded-lg resize-none"
        id="content"
        name="content"
        value={copilotText}
        placeholder="Write your article content here"
        onChange={(event) => setCopilotText(event.target.value)}
      />

      {/* Publish button */}
      <button
        type="submit"
        className="p-4 w-full !bg-slate-800 text-white rounded-lg">Publish</button>
    </form>
  );
}

```

Next, add another file to the components folder, and name it `Header.tsx`. Then add the following code to the file that defines a functional component named `Header` that will render the blogging platform’s navbar.

```tsx
import Link from "next/link";

export default function Header() {
  return (
    <>
      <header className="flex flex-wrap sm:justify-start sm:flex-nowrap z-50 w-full bg-white border-b border-gray-200 text-sm py-3 sm:py-0 ">
        <nav
          className="relative max-w-7xl w-full mx-auto px-4 sm:flex sm:items-center sm:justify-between sm:px-6 lg:px-8"
          aria-label="Global">
          <div className="flex items-center justify-between">
            <Link
              className="flex-none text-xl font-semibold "
              href="/"
              aria-label="Brand">
              AIBlogging
            </Link>
          </div>
          <div id="navbar-collapse-with-animation" className="">
            <div className="flex flex-col gap-y-4 gap-x-0 mt-5 sm:flex-row sm:items-center sm:justify-end sm:gap-y-0 sm:gap-x-7 sm:mt-0 sm:ps-7">
              <Link
                className="flex items-center font-medium text-gray-500 border-2 border-indigo-600 text-center p-2 rounded-md hover:text-blue-600 sm:border-s sm:my-6 "
                href="/writearticle">
                Create Post
              </Link>
            </div>
          </div>
        </nav>
      </header>
    </>
  );
}
```

After that, go to `/[root]/src/app` and create a folder called `writearticle`. Inside the `writearticle` folder, create a file called `page.tsx` file. Then add the following code into the file that imports `Article` and `Header` components. The code then defines a functional component named `WriteArticle` that will render the navbar and the article creation form.

```tsx
import { Article } from "../components/Article";
import Header from "../components/Header";

export default function WriteArticle() {
  return (
    <>
      <Header />
      <Article />
    </>
  );
}

```

Next, go to `/[root]/src/page.tsx` file, and add the following code that defines a functional component named `Home` that renders the blogging platform homepage that will display list of published articles.

```tsx
import Image from "next/image";
import Link from "next/link";
import Header from "./components/Header";

const Home = async () => {
  return (
    <>
      <Header />
      <div className="max-w-[85rem] h-full px-4 py-10 sm:px-6 lg:px-8 lg:py-14 mx-auto">
        <div className="grid sm:grid-cols-2 lg:grid-cols-3 gap-6">
          <Link
            key={""}
            className="group flex flex-col h-full bg-white border border-gray-200 hover:border-transparent hover:shadow-lg transition-all duration-300 rounded-xl p-5 "
            href={""}>
            <div className="aspect-w-16 aspect-h-11">
              <Image
                className="object-cover h-48 w-96 rounded-xl"
                src={`https://source.unsplash.com/featured/?${encodeURIComponent(
                  "hello world"
                )}`}
                width={500}
                height={500}
                alt="Image Description"
              />
            </div>
            <div className="my-6">
              <h3 className="text-xl font-semibold text-gray-800 ">
                Hello World
              </h3>
            </div>
          </Link>
        </div>
      </div>
    </>
  );
};

export default Home;
```

After that, go to the `next.config.js` file and add the following code that allows you to use images from Unsplash as cover images for the published articles.

```jsx
module.exports = {
  images: {
    remotePatterns: [
      {
        protocol: "https",
        hostname: "source.unsplash.com",
      },
    ],
  },
};

```

Finally, run the command `npm run dev` on the command line and then navigate to http://localhost:3000/. Now you should view the blogging platform frontend on your browser, as shown below.


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

## **Integrating The Blogging Platform With The CopilotKit Backend**

In this section, I will walk you through the process of integrating the blogging platform with CopilotKit backend that handles requests from frontend, provides function calling and various LLM backends such as GPT. Also, we will integrate an AI agent named Tavily that can research any topic on the web.

To get started, create a file called `.env.local` in the root directory. Then add the environment variables below in the file that hold your `ChatGPT` and `Tavily` Search API keys.

```
OPENAI_API_KEY="Your ChatGPT API key"
TAVILY_API_KEY="Your Tavily Search API key"
```

To get the ChatGPT API key, navigate to https://platform.openai.com/api-keys.


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

To get the Tavily Search API key, navigate to https://app.tavily.com/home


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

After that, go to `/[root]/src/app` and create a folder called `api`. In the `api` folder, create a folder called `copilotkit`. In the `copilotkit` folder, create a file called `research.ts`. Then Navigate to [this research.ts gist file](https://gist.github.com/TheGreatBonnie/58dc21ebbeeb8cbb08df665db762738c), copy the code, and add it to the **`research.ts`** file

Next, create a file called `route.ts` in the `/[root]/src/app/api/copilotkit` folder. The file will contain code that sets up a backend functionality to process POST requests. It conditionally includes a "research" action that performs research on a given topic.

Now import the following modules at the top of the file.

```tsx
import { CopilotBackend, OpenAIAdapter } from "@copilotkit/backend"; // For backend functionality with CopilotKit.
import { researchWithLangGraph } from "./research"; // Import a custom function for conducting research.
import { AnnotatedFunction } from "@copilotkit/shared"; // For annotating functions with metadata.

```

Below the code above, define a runtime environment variable and a function named `researchAction` that conducts research on a certain topic using the code below.

```tsx
// Define a runtime environment variable, indicating the environment where the code is expected to run.
export const runtime = "edge";

// Define an annotated function for research. This object includes metadata and an implementation for the function.
const researchAction: AnnotatedFunction<any> = {
  name: "research", // Function name.
  description: "Call this function to conduct research on a certain topic. Respect other notes about when to call this function", // Function description.
  argumentAnnotations: [ // Annotations for arguments that the function accepts.
    {
      name: "topic", // Argument name.
      type: "string", // Argument type.
      description: "The topic to research. 5 characters or longer.", // Argument description.
      required: true, // Indicates that the argument is required.
    },
  ],
  implementation: async (topic) => { // The actual function implementation.
    console.log("Researching topic: ", topic); // Log the research topic.
    return await researchWithLangGraph(topic); // Call the research function and return its result.
  },
};

```

Then add the code below under the code above to define an asynchronous function that handles POST requests.

```tsx
// Define an asynchronous function that handles POST requests.
export async function POST(req: Request): Promise<Response> {
  const actions: AnnotatedFunction<any>[] = []; // Initialize an array to hold actions.

  // Check if a specific environment variable is set, indicating access to certain functionality.
  if (process.env["TAVILY_API_KEY"]) {
    actions.push(researchAction); // Add the research action to the actions array if the condition is true.
  }

  // Instantiate CopilotBackend with the actions defined above.
  const copilotKit = new CopilotBackend({
    actions: actions,
  });

  // Use the CopilotBackend instance to generate a response for the incoming request using an OpenAIAdapter.
  return copilotKit.response(req, new OpenAIAdapter());
}
```

## **Integrating The Blogging Platform With The CopilotKit Frontend**

In this section, I will walk you through the process of integrating the blogging platform with the CopilotKit frontend to facilitate blog article research and article outline generation. We will use a chatbot sidebar component, a copilot textarea component, a useMakeCopilotReadable hook for providing app-state & other information to the Copilot, and a useCopilotAction hook for providing actions the Copilot can call

To get started, import the `useMakeCopilotReadable`, `useCopilotAction`, `CopilotTextarea`, and `HTMLCopilotTextAreaElement` hooks at the top of the `/[root]/src/app/components/Article.tsx` file.

```tsx
import {
  useMakeCopilotReadable,
  useCopilotAction,
} from "@copilotkit/react-core";
import {
  CopilotTextarea,
  HTMLCopilotTextAreaElement,
} from "@copilotkit/react-textarea";
```

Inside the Article function, below the state variables, add the following code that uses the `useMakeCopilotReadable` hook to add the article outline that will be generated as context for the in-app chatbot. The hook makes the article outline readable to the copilot.

```tsx
useMakeCopilotReadable("Blog article outline: " + JSON.stringify(articleOutline));

```

Below the `useMakeCopilotReadable` hook, use the code below to create a reference called `copilotTextareaRef` to a textarea element called `HTMLCopilotTextAreaElement`.

```tsx
const copilotTextareaRef = useRef<HTMLCopilotTextAreaElement>(null);
```

Below the code above, add the following code that uses the `useCopilotAction` hook to set up an action called `researchBlogArticleTopic` which will enable research on a given topic for a blog article. The action takes in two parameters called `articleTitle` and `articleOutline` which enables generation of an article title and outline.

The action contains a handler function that generates an article title and outline based on a given topic. Inside the handler function, `articleOutline` state is updated with the newly generated outline while `articleTitle` state is updated with the newly generated title, as shown below.

```tsx
useCopilotAction(
    {
      name: "researchBlogArticleTopic",
      description: "Research a given topic for a blog article.",
      parameters: [
        {
          name: "articleTitle",
          type: "string",
          description: "Title for a blog article.",
          required: true,
        },
        {
          name: "articleOutline",
          type: "string",
          description:"Outline for a blog article that shows what the article covers.",
          required: true,
        },
      ],
      handler: async ({ articleOutline, articleTitle }) => {
        setArticleOutline(articleOutline);

        setArticleTitle(articleTitle);
      },
    },
    []
  );
```

Below the code above, go to the form component and add the following `CopilotTextarea` element that will enable you to add completions, insertions, and edits to your article content. 

```tsx
<CopilotTextarea
        value={copilotText}
        ref={copilotTextareaRef}
        placeholder="Write your article content here"
        onChange={(event) => setCopilotText(event.target.value)}
        className="p-4 w-full aspect-square font-bold text-xl bg-slate-800 text-white rounded-lg resize-none"
        placeholderStyle={{
          color: "white",
          opacity: 0.5,
        }}
        autosuggestionsConfig={{
          textareaPurpose: articleTitle,
          chatApiConfigs: {
            suggestionsApiConfig: {
              forwardedParams: {
                max_tokens: 5,
                stop: ["\n", ".", ","],
              },
            },
            insertionApiConfig: {},
          },
          debounceTime: 250,
        }}
      />
```

Then add the Tailwindcss hidden class to the Textarea for article content, as shown below. The textarea will hold the article's content and enable it to be inserted into a database once the article is published.

```tsx
{/* Textarea for article content */}
      <textarea
        className="p-4 w-full aspect-square font-bold text-xl bg-slate-800 text-white rounded-lg resize-none hidden"
        id="content"
        name="content"
        value={copilotText}
        placeholder="Write your article content here"
        onChange={(event) => setCopilotText(event.target.value)}
      />
```

After that, go to `/[root]/src/app/writearticle/page.tsx` file and import CopilotKit frontend packages and styles at the top using the code below.

```tsx
import { CopilotKit } from "@copilotkit/react-core";
import { CopilotSidebar } from "@copilotkit/react-ui";
import "@copilotkit/react-ui/styles.css";
import "@copilotkit/react-textarea/styles.css";
```

Then use `CopilotKit` and `CopilotSidebar` to wrap the Article component, as shown below. The `CopilotKit` component specifies the URL for CopilotKit's backend endpoint (`/api/copilotkit/openai/`) while the `CopilotSidebar` renders the in-app chatbot that you can give prompts to research any topic for an article.

```tsx
export default function WriteArticle() {
  return (
    <>
      <Header />
      <CopilotKit url="/api/copilotkit">
        <CopilotSidebar
          instructions="Help the user research a blog article topic."
          defaultOpen={true}
          labels={{
            title: "Blog Article Copilot",
            initial:
              "Hi you! 👋 I can help you research any topic for a blog article.",
          }}
          clickOutsideToClose={false}>
          <Article />
        </CopilotSidebar>
      </CopilotKit>
    </>
  );
}
```

After that, run the development server and navigate to http://localhost:3000/writearticle. You should see that the in-app chatbot was integrated into the Blogging platform.


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

Give the chatbot on the right side a prompt like, “research a blog article topic on generative AI and give me the article outline.” The chatbot will start researching the topic and then generate a blog title. 

When you start writing on the editor, you should see content autosuggestions, as shown below.


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

## **Integrating The Blogging Platform With Supabase Database**

In this section, I will walk you through the process of integrating the blogging platform with Supabase database to insert and fetch blog article data.

To get started, navigate to [supabase.com](http://supabase.com) and click the Start your project button on the home page.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/95n06a6aj55r5w959gz6.png)

Then create a new project called AiBloggingPlatform, as shown below.


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

Once the project is created, add your Supabase URL and API key to environment variables in the env.local file, as shown below.

```tsx
NEXT_PUBLIC_SUPABASE_URL=”Your Supabase URL”
NEXT_PUBLIC_SUPABASE_ANON_KEY=”Your Supabase API Key”
```

After that, go to your project’s dashboard on Supabase and open the SQL Editor section. Then add the following SQL code to the editor and click CTRL + Enter keys to create a table called articles. The articles table has id, title, and content rows.

```sql
create table if not exists
  articles (
    id bigint primary key generated always as identity,
    title text,
    content text
  );
```

Once the table is created, you should get a success message, as shown below.


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

After that, go to `/[root]/src/` folder and create a folder called `utils`. Inside the `utils` folder, create a file called `supabase.ts` and add the following code that creates and returns a Supabase client.

```tsx
// Importing necessary functions and types from the Supabase SSR package
import { createServerClient, type CookieOptions } from '@supabase/ssr'

// Define a function named 'supabase' that takes a 'CookieOptions' object as input
export const supabase = (cookies: CookieOptions) => {
    // Retrieve cookies from the provided 'CookieOptions' object
    const cookieStore = cookies()

    // Create and return a Supabase client configured with environment variables and cookie handling
    return createServerClient(
        // Retrieve Supabase URL from environment variables
        process.env.NEXT_PUBLIC_SUPABASE_URL!,
        // Retrieve Supabase anonymous key from environment variables
        process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
        {
            cookies: {
                // Define a custom 'get' function to retrieve cookies by name from the cookie store
                get(name: string) {
                    return cookieStore.get(name)?.value
                },
            },
        }
    )
}

```

Then go to `/[root]/src/app` folder and create a folder called `serveractions`. In the `serveractions` folder, create a file called `AddArticle.ts` and add the following code that inserts blog article data into the Supabase database.

```tsx
// Importing necessary functions and modules for server-side operations
"use server";
import { createServerComponentClient } from "@supabase/auth-helpers-nextjs";
import { cookies } from "next/headers";
import { redirect } from "next/navigation";

// Define an asynchronous function named 'addArticle' that takes form data as input
export async function addArticle(formData: any) {
  // Extract title and content from the provided form data
  const title = formData.get("title");
  const content = formData.get("content");

  // Retrieve cookies from the HTTP headers
  const cookieStore = cookies();

  // Create a Supabase client configured with the provided cookies
  const supabase = createServerComponentClient({ cookies: () => cookieStore });

  // Insert the article data into the 'articles' table on Supabase
  const { data, error } = await supabase.from("articles").insert([
    {
      title,
      content,
    },
  ]);

  // Check for errors during the insertion process
  if (error) {
    console.error("Error inserting data", error);
    return;
  }

  // Redirect the user to the home page after successfully adding the article
  redirect("/");

  // Return a success message
  return { message: "Success" };
}
```

After that, go to `/[root]/src/app/components/Article.tsx` file and import the `addArticle` function.

```tsx
import { addArticle } from "../serveractions/AddArticle";
```

Then add the `addArticle` function as the form action parameter, as shown below.

```tsx
// Form element for article input
    <form
      action={addArticle}
      className="w-full h-full gap-10 flex flex-col items-center p-10"> 
      
      </form>
```

After that, navigate to  http://localhost:3000/writearticle, research the topic of your choice, add article content, and then click the publish button at the bottom to publish the article. 

Then go to your project’s dashboard on Supabase and navigate to the Table Editor section. You should see that your article data was inserted into the Supabase database, as shown below.


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

Next, go to `/[root]/src/app/page.tsx` file and import cookies and supabase packages at the top.

```tsx
import { cookies } from "next/headers";
import { supabase } from "@/utils/supabase";
```

Then inside the Home function, add the following code that fetches articles data from Supabase database.

```sql
const { data: articles, error } = await supabase(cookies).from('articles').select('*')
```

After that, update the elements code as shown below to render the published articles on the blogging platform homepage.

```tsx
return (
    <>
      <Header />
      <div className="max-w-[85rem] h-full px-4 py-10 sm:px-6 lg:px-8 lg:py-14 mx-auto">
        <div className="grid sm:grid-cols-2 lg:grid-cols-3 gap-6">
          {articles?.map((post: any) => (
            <Link
              key={post.id}
              className="group flex flex-col h-full bg-white border border-gray-200 hover:border-transparent hover:shadow-lg transition-all duration-300 rounded-xl p-5 "
              href={`/posts/${post.id}`}>
              <div className="aspect-w-16 aspect-h-11">
                <Image
                  className="object-cover h-48 w-96 rounded-xl"
                  src={`https://source.unsplash.com/featured/?${encodeURIComponent(
                    post.title
                  )}`}
                  width={500}
                  height={500}
                  alt="Image Description"
                />
              </div>
              <div className="my-6">
                <h3 className="text-xl font-semibold text-gray-800 ">
                  {post.title}
                </h3>
              </div>
            </Link>
          ))}
        </div>
      </div>
    </>
  );
```

Then navigate to  [http://localhost:3000](http://localhost:3000/writearticle) and you should see the article you published, as shown below.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/429bilwyje2a4xh0il5n.png)

After that, go to `/[root]/src/app` folder and create a folder called `[id].` In the `[id]` folder, create a file called `page.tsx` and import the following packages and components at the top.

```tsx
import { supabase } from '@/utils/supabase';
import { cookies } from "next/headers";
import Header from '@/app/components/Header';
```

Below the imports, define an asynchronous function named 'getArticles' that retrieves article data from supabase database based on id parameter, as shown below.

```tsx
// Define an asynchronous function named 'getArticles' that retrieves article data based on the provided parameters
async function getArticles(params: any) {
    // Extract the 'id' parameter from the provided 'params' object
    const { id } = params
  
    // Retrieve article data from Supabase database where the 'id' matches the provided value
    const { data, error } = await supabase(cookies)
        .from('articles')
        .select('*')
        .eq('id', id)
        .single();
    
    // Return the retrieved data
    return data
  }
```

Below the code above, define a function called 'Post' that takes 'params' as props, as shown below.

```tsx
// Define a default asynchronous function named 'Post' that takes 'params' as props
export default async function Post({ params }: { params: any }) {
  // Retrieve the post data asynchronously based on the provided 'params'
  const post = await getArticles(params);

  // Return JSX to render the post details
  return (
    <>
      {/* Render the header component */}
      <Header />
      {/* Main content wrapper */}
      <div className="max-w-3xl px-4 pt-6 lg:pt-10 pb-12 sm:px-6 lg:px-8 mx-auto">
        <div className="max-w-2xl">
          <div className="space-y-5 md:space-y-8">
            <div className="space-y-3">
              {/* Render the post title */}
              <h2 className="text-2xl font-bold md:text-3xl dark:text-white">
                {/* Render the post title only if 'post' is truthy */}
                {post && post.title}
              </h2>
              {/* Render the post content */}
              <p className="text-lg text-gray-800 dark:text-gray-200">
                {/* Render the post content only if 'post' is truthy */}
                {post && post.content}
              </p>
            </div>
          </div>
        </div>
      </div>
    </>
  );
}
```

After that, navigate to  [http://localhost:3000](http://localhost:3000/writearticle) and click the article displayed on the blogging platform homepage.

You should then be redirected to the article’s content, as shown below.


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

## Conclusion

In conclusion, you can use CopilotKit to build in-app AI chatbots that can see the current app state and take action inside your app. The AI chatbot can talk to your app frontend, backend, and third-party services.

For the full source-code: https://github.com/TheGreatBonnie/aipoweredblog