There is lag when its try to get a respond to write at React marker

The AI ​​speech should be written quickly and not in this way, so that the site stops for about 5 seconds until the AI ​​finishes its speech in a strange way, as I will attach in the video. This is a very annoying reality. I have been in this problem for a week and I cannot find a solution. I am even starting to lose hope in the project. I will attach all the codes and all the pictures that are posted. What is happening?

[image]

/* eslint-disable @typescript-eslint/no-unused-vars */
"use client"

import { Chat } from "@/components/ui/chat";
import { AppSidebar } from "../components/app-sidebar"
import { Icon } from "@iconify/react";
import {
  Breadcrumb,
  BreadcrumbItem,
  BreadcrumbLink,
  BreadcrumbList,
  BreadcrumbPage,
  BreadcrumbSeparator,
} from "@/components/ui/breadcrumb"
import { Separator } from "@/components/ui/separator"
import { Form } from '@heroui/form';
import { Badge } from '@heroui/badge'
import { Button, Card, CardBody, CardFooter, CardHeader, Divider, Link } from '@heroui/react';
import Image from "next/image";
import { Input, Textarea } from '@heroui/input'
import { Avatar } from '@heroui/avatar';
import { Tooltip } from '@heroui/tooltip';
import ProseMirrorMarkdownEditor from "@/components/ProseMirrorMarkdownEditor";
import { SidebarInset, SidebarProvider, SidebarTrigger, useSidebar } from "@/components/ui/sidebar"
import { useChat } from "ai/react"
import { useSession } from "next-auth/react";
import { Spinner } from "@heroui/spinner"
import Cookies from "js-cookie";
import { useEffect, useState } from "react";
import { Logo, SideBarPhoneIcon, SideBarPhoneIcon2 } from "@/components/icons";
import SyntaxHighlighter from "react-syntax-highlighter";
import { dark } from "react-syntax-highlighter/dist/esm/styles/prism";
import ReactMarkdown from "react-markdown";
import remarkGfm from "remark-gfm";
import { ButtonGroup, Chip, Dropdown, DropdownItem, DropdownMenu, DropdownTrigger } from "@heroui/react";
import { useTheme } from "next-themes";
import Header from "@/components/Header";
import { toast } from "sonner";
import { useCopyToClipboard } from "@/hooks/use-copy-to-clipboard";

export default function Page() {
  const {
    messages,
    input,
    handleInputChange,
    handleSubmit,
    append,
    reload,
    error,
    isLoading,
    stop,
  } = useChat()
  const session = useSession()
  const { open, toggleSidebar, isMobile } = useSidebar()
  const [selectedAi, setSelectedAi] = useState("DeepSeek R1")
  const { theme, setTheme } = useTheme();
  const savedTheme = Cookies.get("theme");
  const [copied, setcopy] = useState(false)
  const [src, setsrc] = useState("/LogoBlack.png")
  const handleCopy = (input: string) => {
    if(copied) return;
    navigator.clipboard.writeText(input)
      .then(() => {
        setcopy(true)
        setTimeout(() => {
          setcopy(false);
        }, 5000);
      })
      .catch((err) => {
        toast.error("Can't copy :(")
      });
  };

  return (
    <>
      <AppSidebar />

      <SidebarInset>
        <header className="flex h-16 shrink-0 sticky top-0 z-30 items-center gap-2 transition-[width,height] ease-linear group-has-[[data-collapsible=icon]]/sidebar-wrapper:h-12 border-b-small bg-white dark:bg-background dark:border-sidebar-border border-gray-200">
          <div className="flex items-center gap-2 px-4">
            {!isMobile ? (
              open ? null : (
                <>
                  <Button
                    isIconOnly
                    variant='light'
                    onPress={toggleSidebar}
                  >
                    {
                      isMobile ? <SideBarPhoneIcon2 size={25} /> : <Icon icon="solar:siderbar-outline" width={25} />
                    }
                  </Button>
                  <Divider orientation="vertical" className="mr-2 h-4" />
                </>
              )
            ) : (
              <>
                <Button
                  isIconOnly
                  variant='light'
                  onPress={toggleSidebar}
                >
                  {
                    isMobile ? <SideBarPhoneIcon2 size={25} /> : <Icon icon="solar:siderbar-outline" width={25} />
                  }
                </Button>
                <Separator orientation="vertical" className="mr-2 h-4" /></>
            )}
            <Dropdown>
              <DropdownTrigger>
                <Button className="sm:max-w-none max-w-24 text-foreground-400" startContent={<Icon icon="svg-spinners:blocks-scale" />} variant='light' >{selectedAi}</Button>
              </DropdownTrigger>
              <DropdownMenu aria-label="Example with disabled actions" selectedKeys={[selectedAi]}>
                <DropdownItem onSelect={() => setSelectedAi("Gemini")} endContent={<Chip size='sm' radius='lg' color='warning' className="text-[#C9A9E9] bg-[#643b8d]">Pro</Chip>} key="gemini">Gemini</DropdownItem>
                <DropdownItem onSelect={() => setSelectedAi("DeepSeek R1")} endContent={<Chip size='sm' radius='full' className="text-[#17C946] bg-[#17c94680]" color="success">New</Chip>} key="deepseek">DeepSeek R1</DropdownItem>
              </DropdownMenu>
            </Dropdown>
          </div>
        </header>
        {messages.length === 0 ? (
          <div id="scroll-auto">
            <div className="flex-grow flex flex-col items-center gap-4 justify-center min-h-[656px] pb-[5%]">

              <div className="justify-start items-start">
                <Logo size={150} />
              </div>
              <div className="text-center">
                <h1 className="text-4xl font-bold ">Hi, I&apos;m EgyAI.</h1>
                <p className="text-xl text-gray-600 mt-4">How can I help you today?</p>
              </div>
              <div
                id="search-bar"
                className="relative w-full flex flex-col justify-center items-center gap-4"
              >
                <Textarea
                  className="max-w-[800px] flex flex-col justify-between w-full rounded-2xl border-medium px-3 py-2 pt-3 gap-1.5 hover:border-gray-400 transition-colors duration-200"
                  onChange={handleInputChange}
                  autoFocus
                  value={input}
                  placeholder="Ask EgyAI..."
                  onKeyDown={(e) => {
                    if (e.key === 'Enter' && !e.shiftKey) {
                      e.preventDefault();
                      handleSubmit();
                    }
                  }}
                  endContent={
                    <>
                      <Button size="sm" onPress={() => handleSubmit()} variant='light' fullWidth isIconOnly><Icon width={32} icon="solar:link-circle-bold" /></Button>
                      <Tooltip closeDelay={3} content={`${input.length === 0 ? "Empty Message" : ""}`}>
                        <div style={{ cursor: `${input.length === 0 ? 'not-allowed' : ""}` }}>
                          <Button isDisabled={input.length === 0} size="sm" onPress={() => (isLoading ? stop() : handleSubmit())} variant='light' fullWidth isIconOnly><Icon width={32} icon={isLoading ? "solar:stop-circle-bold" : "solar:round-arrow-up-bold"} /></Button>
                        </div>
                      </Tooltip>
                    </>
                  }
                  classNames={{
                    base: "border-none"
                  }}
                />
              </div>
            </div>
          </div>
        ) : (<>
          <div className="flex flex-col overflow-auto px-3 w-full mx-auto h-full justify-between">
            <div className="w-full md:max-w-4xl mx-auto pt-4 pb-2 px-4">
              <div className="flex gap-12">
                <div className="flex-grow flex flex-col gap-1 relative items-center max-w-full">
                  <div id="qa-container">
                    <div id="message" className="flex gap-12 flex-col min-h-0">
                      <div className="flex-grow flex flex-col gap-2">
                        <div className="flex-grow flex flex-col gap-2">
                          {messages.map(message => (
                            <div key={message.id} className="w-full">
                              {message.role === 'user' ?
                                <>
                                  <article
                                    className="w-full text-token-text-primary focus-visible:outline-2 focus-visible:outline-offset-[-4px]"
                                    dir="auto"
                                    data-testid="conversation-turn-2"
                                    data-scroll-anchor="false"
                                  >
                                    <h5 className="sr-only">User said:</h5>
                                    <div className="m-auto text-base py-[18px] px-6">
                                      <div className="mx-auto flex flex-1 gap-4 text-base md:gap-5 lg:gap-6 md:max-w-3xl lg:max-w-[40rem] xl:max-w-[48rem]">
                                        <div className="group/conversation-turn relative flex w-full min-w-0 flex-col @xs/thread:px-0 @sm/thread:px-1.5 @md/thread:px-4">
                                          <div className="flex-col gap-1 md:gap-3">
                                            <div className="flex max-w-full flex-col flex-grow">
                                              <div
                                                data-message-author-role="user"
                                                data-message-id={message.id}
                                                dir="auto"
                                                className="min-h-8 text-message overflow-x-hidden flex w-full flex-col items-end gap-2 whitespace-normal break-words text-start [.text-message+&]:mt-5"
                                              >
                                                <div className="flex w-full flex-col gap-1 empty:hidden items-end rtl:items-start">
                                                  <Card className="relative w-fit max-w-[70vw] sm:max-w-[70%] rounded-3xl px-5 py-2.5">
                                                    {message.content}
                                                  </Card>
                                                </div>
                                              </div>
                                            </div>
                                          </div>
                                        </div>
                                      </div>
                                    </div>
                                  </article>
                                  <div className="min-w-[70vw]" id="look-saver cuz i'm noob at frontend"></div>
                                </>
                                :

                                <article
                                  className="w-full text-token-text-primary focus-visible:outline-2 focus-visible:outline-offset-[-4px]"
                                  dir="auto"
                                  data-testid="conversation-turn-3"
                                  data-scroll-anchor="true"
                                >
                                  <h6 className="sr-only">EgyAi said:</h6>
                                  <div className="m-auto text-base py-[18px] px-6">
                                    <div className="mx-auto flex flex-1 gap-4 text-base md:gap-5 lg:gap-6 md:max-w-3xl lg:max-w-[40rem] xl:max-w-[48rem]">
                                      <div className="group/conversation-turn relative flex w-full min-w-0 flex-col agent-turn @xs/thread:px-0 @sm/thread:px-1.5 @md/thread:px-4">
                                        <div className="flex-col gap-1 md:gap-3">
                                          <div className="flex max-w-full flex-col flex-grow">
                                            <div
                                              data-message-author-role="assistant"
                                              data-message-id={message.id}
                                              dir="auto"
                                              className="min-h-8 text-message flex w-full flex-col items-end gap-2 whitespace-normal break-words text-start [.text-message+&]:mt-5"
                                              data-message-model-slug={selectedAi}
                                            >
                                                   <ProseMirrorMarkdownEditor initialMarkdown={message.content } />

                                            </div>
                                          </div>
                                          <div className="mb-2 flex gap-3 empty:hidden -ml-2">
                                            <div className="items-center justify-start rounded-xl p-1 flex">
                                              <div className="flex items-center">
                                                <Tooltip content="SOON" placement='bottom' closeDelay={3}>
                                                  <Button disabled isIconOnly fullWidth size="sm" variant='light'><Icon width={23} icon="solar:volume-loud-linear" /></Button>
                                                </Tooltip>
                                                <Tooltip content="Copy" placement='bottom' closeDelay={3}>
                                                  <Button onPress={() => handleCopy(message.content)} isIconOnly fullWidth size="sm" variant='light'>{copied ? <Icon width={23} icon="solar:check-read-linear" /> : <Icon width={23} icon="solar:copy-linear" />}</Button>
                                                </Tooltip>
                                                <div className="flex">
                                                  <Tooltip content="Good response" placement='bottom' closeDelay={3}>
                                                    <Button disabled isIconOnly fullWidth size="sm" variant='light'><Icon width={23} icon="solar:like-linear" /></Button>
                                                  </Tooltip>
                                                  <Tooltip content="Bad response" placement='bottom' closeDelay={3}>
                                                    <Button disabled isIconOnly fullWidth size="sm" variant='light'><Icon width={23} icon="solar:dislike-linear" /></Button>
                                                  </Tooltip>
                                                </div>
                                                <span className="hidden" />
                                                <Tooltip content="Reload response" placement='bottom' closeDelay={3}>
                                                  <Button onPress={() => reload()} isIconOnly fullWidth size="sm" variant='light'><Icon width={23} icon="solar:refresh-linear" /></Button>
                                                </Tooltip>
                                              </div>
                                            </div>
                                          </div>
                                          <div className="pr-2 lg:pr-0" />
                                          <div className="mt-3 w-full empty:hidden">
                                            <div className="text-center" />
                                          </div>
                                        </div>
                                      </div>
                                    </div>
                                  </div>
                                </article>}
                            </div>
                          ))}
                        </div>
                      </div>
                    </div>
                  </div>
                </div>
              </div>
            </div>

          </div>
          <div className="sticky bottom-0 mx-auto md:max-w-4xl flex justify-center z-30 w-full px-4 py-4 bg-background">
            <Textarea
              className="max-w-[800px] flex flex-col justify-between w-full rounded-2xl border-medium px-3 py-2 pt-3 gap-1.5 hover:border-gray-400 transition-colors duration-200"
              onChange={handleInputChange}
              autoFocus
              placeholder="Ask EgyAI..."
              value={input}
              onKeyDown={(e) => {
                if (e.key === 'Enter' && !e.shiftKey) {
                  e.preventDefault();
                  handleSubmit();
                }
              }}
              description="Keep in mind EgyAI can make mistakes,Check important information"
              endContent={
                <>
                  <Button size="sm" onPress={() => handleSubmit()} variant='light' fullWidth isIconOnly><Icon width={32} icon="solar:link-circle-bold" /></Button>
                  <Tooltip closeDelay={3} content={`${(isLoading ? false : input.length === 0) ? "Empty Message" : ""}`}>
                    <div style={{ cursor: `${(isLoading ? false : input.length === 0) ? 'not-allowed' : ""}` }}>
                      <Button isDisabled={(isLoading ? false : input.length === 0)} size="sm" onPress={() => (isLoading ? stop() : handleSubmit())} variant='light' fullWidth isIconOnly><Icon width={32} icon={isLoading ? "solar:stop-circle-bold" : "solar:round-arrow-up-bold"} /></Button>
                    </div>
                  </Tooltip>
                </>
              }

              classNames={{
                description: "text-center",
                base: "border-none"
              }}
            />
          </div>
        </>)}
      </SidebarInset >
    </>
  )
}

MarkDown

import React, { useEffect, useRef } from "react";
import { EditorState } from "prosemirror-state";
import { EditorView } from "prosemirror-view";
import { schema } from "prosemirror-schema-basic";
import { defaultMarkdownParser } from "prosemirror-markdown";
import hljs from "highlight.js"; // Import highlight.js
import "highlight.js/styles/github-dark.css"; // Choose any theme

// Function to apply syntax highlighting to code blocks
function addCodeHighlighting(view) {
  const codeBlocks = view.dom.querySelectorAll("pre code");
  codeBlocks.forEach((block) => {
    hljs.highlightElement(block); // Apply syntax highlighting
  });
}

// Create a ProseMirror state
function createEditorState(markdownContent) {
  const doc = defaultMarkdownParser.parse(markdownContent);
  return EditorState.create({ doc });
}

// React component for a read-only ProseMirror Markdown viewer
function ProseMirrorMarkdownViewer({ initialMarkdown }) {
  const editorRef = useRef(null);
  const viewRef = useRef(null);

  useEffect(() => {
    // Initialize the editor
    const state = createEditorState(initialMarkdown);
    const view = new EditorView(editorRef.current, {
      state,
      dispatchTransaction(transaction) {
        const newState = view.state.apply(transaction);
        view.updateState(newState);
        addCodeHighlighting(view); // Highlight after updates
      },
      editable: () => false, // Disable editing
    });

    viewRef.current = view;
    addCodeHighlighting(view); // Initial highlighting

    // Cleanup on unmount
    return () => {
      view.destroy();
    };
  }, [initialMarkdown]);

  return <div ref={editorRef} className="prosemirror-container" />;
}

export default ProseMirrorMarkdownViewer;
{
  "name": "egpyt-gpt-chat",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint"
  },
  "dependencies": {
    "@ai-sdk/deepseek": "^0.1.8",
    "@ai-sdk/fal": "^0.0.2",
    "@ai-sdk/fireworks": "^0.1.8",
    "@ai-sdk/google": "^1.1.8",
    "@ai-sdk/groq": "^1.1.7",
    "@ai-sdk/openai": "^1.1.9",
    "@codemirror/highlight": "^0.19.8",
    "@codemirror/lang-python": "^6.1.7",
    "@codemirror/theme-one-dark": "^6.1.2",
    "@fal-ai/client": "^1.2.3",
    "@heroui/react": "^2.6.14",
    "@iconify/react": "^5.2.0",
    "@mdx-js/loader": "^3.1.0",
    "@mdx-js/react": "^3.1.0",
    "@nanostores/react": "github:ai/react",
    "@next/env": "^15.1.6",
    "@next/mdx": "^15.1.6",
    "@prose-ui/core": "^1.0.4",
    "@prose-ui/next": "^1.0.4",
    "@radix-ui/react-avatar": "^1.1.2",
    "@radix-ui/react-collapsible": "^1.1.2",
    "@radix-ui/react-dialog": "^1.1.5",
    "@radix-ui/react-dropdown-menu": "^2.1.5",
    "@radix-ui/react-separator": "^1.1.1",
    "@radix-ui/react-slot": "^1.1.1",
    "@radix-ui/react-switch": "^1.1.2",
    "@radix-ui/react-tooltip": "^1.1.7",
    "@react-aria/ssr": "^3.9.7",
    "@tabler/icons-react": "^3.29.0",
    "@tiptap/core": "^2.11.5",
    "@tiptap/react": "^2.11.5",
    "@tiptap/starter-kit": "^2.11.5",
    "@types/mdx": "^2.0.13",
    "@types/react-syntax-highlighter": "^15.5.13",
    "ai": "^4.1.16",
    "class-variance-authority": "^0.7.1",
    "clsx": "^2.1.1",
    "codemirror": "^6.0.1",
    "date-fns": "^4.1.0",
    "framer-motion": "^11.18.2",
    "geist": "^1.3.1",
    "highlight.js": "^11.11.1",
    "js-cookie": "^3.0.5",
    "lucide-react": "^0.474.0",
    "next": "14.2.16",
    "next-auth": "^4.24.11",
    "next-themes": "^0.4.4",
    "react": "^18",
    "react-dom": "^18",
    "react-markdown": "^9.0.3",
    "react-remarkable": "^1.1.3",
    "react-resizable": "^3.0.5",
    "react-syntax-highlighter": "^15.6.1",
    "rehype-highlight": "^7.0.2",
    "remark-gfm": "^4.0.0",
    "remarkable-react": "^1.4.3",
    "remeda": "^2.20.0",
    "shiki": "^2.3.2",
    "sonner": "^1.7.4",
    "tailwind-merge": "^3.0.1",
    "tailwindcss-animate": "^1.0.7",
    "tiptap-markdown": "^0.3.4",
    "usehooks-ts": "^3.1.1"
  },
  "devDependencies": {
    "@iconify-json/solar": "^1.2.2",
    "@types/js-cookie": "^3.0.6",
    "@types/node": "^20",
    "@types/react": "^18",
    "@types/react-dom": "^18",
    "@types/react-resizable": "^3.0.8",
    "@types/remarkable": "^2.0.8",
    "eslint": "^8",
    "eslint-config-next": "14.2.16",
    "postcss": "^8",
    "tailwindcss": "^3.4.1",
    "typescript": "^5"
  }
}

This is likely a performance issue due to how you are rendering the AI response.

Check out our memoization recipe that improve performance and reduce the number of re-renders.

1 Like

thanks its working good now

1 Like

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.