Components / AI Chat Interfaces

AI Chat Window

A full-featured AI chat window with message history, streaming response animation, and conversation management.

Live Preview — ai-chat-window-01

AI Chat Window

C
Welcome! I'm ChatGPT. Ask me anything.

Installation

Dependencies

Install the core libraries required for this component.

npm
npm install react

Shadcn UI Setup

Add the necessary Shadcn UI primitives.

npm
npx shadcn-ui@latest add card scroll-area avatar button textarea select separator

Source Code

ai-chat-window-01.tsx
import { useState, useEffect, useRef } from "react";
import { Card, CardContent } from "@/components/ui/card";
import { ScrollArea } from "@/components/ui/scroll-area";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { Button } from "@/components/ui/button";
import { Textarea } from "@/components/ui/textarea";
import {
    Select,
    SelectTrigger,
    SelectValue,
    SelectContent,
    SelectItem,
} from "@/components/ui/select";
import { Separator } from "@/components/ui/separator";

export default function AIChatWindow01() {
    const [messages, setMessages] = useState<
        { id: number; author: string; text: string; avatar: string }[]
    >([
        {
            id: 1,
            author: "ChatGPT",
            text: "Welcome! I'm ChatGPT. Ask me anything.",
            avatar: "https://i.pravatar.cc/40?img=12",
        },
    ]);

    const [inputMessage, setInputMessage] = useState("");
    const [selectedModel, setSelectedModel] = useState("chatgpt");
    const [searchEnabled, setSearchEnabled] = useState(false);
    const [isStreaming, setIsStreaming] = useState(false);
    const scrollRef = useRef<HTMLDivElement>(null);

    // Auto-scroll to bottom on new message
    useEffect(() => {
        scrollRef.current?.scrollTo({ top: scrollRef.current.scrollHeight });
    }, [messages, isStreaming]);

    const handleSend = () => {
        if (!inputMessage.trim()) return;
        const newMessage = {
            id: messages.length + 1,
            author: "User",
            text: inputMessage,
            avatar: "https://i.pravatar.cc/40?img=5",
        };
        setMessages((prev) => [...prev, newMessage]);
        setInputMessage("");

        // Simulate AI streaming response
        setIsStreaming(true);
        setTimeout(() => {
            const aiResponse = {
                id: messages.length + 2,
                author: "ChatGPT",
                text: `Response from ${selectedModel} ${searchEnabled ? "(with search context)" : ""
                    }`,
                avatar: "https://i.pravatar.cc/40?img=12",
            };
            setMessages((prev) => [...prev, aiResponse]);
            setIsStreaming(false);
        }, 1200);
    };

    const handleKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
        if (e.key === "Enter" && !e.shiftKey) {
            e.preventDefault();
            handleSend();
        }
    };

    const handleClearConversation = () => {
        setMessages([]);
    };

    return (
        <Card className="border-0 flex flex-col">
            {/* Header */}
            <div className="flex items-center justify-between p-2 ">
                <h3 className="font-semibold">AI Chat Window</h3>
                <div className="flex gap-2">
                    <Button onClick={handleClearConversation}>
                        New Chat
                    </Button>
                    <Select onValueChange={setSelectedModel} defaultValue={selectedModel}>
                        <SelectTrigger className="w-[140px]">
                            <SelectValue placeholder="Select model" />
                        </SelectTrigger>
                        <SelectContent>
                            <SelectItem value="chatgpt">ChatGPT</SelectItem>
                            <SelectItem value="gpt4">GPT-4</SelectItem>
                            <SelectItem value="custom-llm">Custom LLM</SelectItem>
                        </SelectContent>
                    </Select>
                    <Button
                        variant={searchEnabled ? "default" : "outline"}
                        onClick={() => setSearchEnabled((prev) => !prev)}
                    >
                        {searchEnabled ? "Search On" : "Search"}
                    </Button>
                </div>
            </div>

            {/* Message history */}
            <CardContent className="p-2 flex-1 overflow-hidden">
                <ScrollArea
                    ref={scrollRef}
                    className="h-full gap-3 mb-4 pr-2"
                >
                    {messages.map((msg) => (
                        <div
                            key={msg.id}
                            className={`flex items-start gap-3 ${msg.author === "ChatGPT" ? "flex-row" : "flex-row-reverse"
                                }`}
                        >
                            <Avatar>
                                <AvatarImage src={msg.avatar} />
                                <AvatarFallback>{msg.author[0]}</AvatarFallback>
                            </Avatar>
                            <div className="flex flex-col">
                                <div className="p-2 border rounded-lg border-border max-w-xs">
                                    {msg.text}
                                </div>
                            </div>
                        </div>
                    ))}
                    {isStreaming && (
                        <div className="flex items-start gap-3">
                            <Avatar>
                                <AvatarImage src="https://i.pravatar.cc/40?img=12" />
                                <AvatarFallback>A</AvatarFallback>
                            </Avatar>
                            <div className="p-2 border rounded-lg border-border max-w-xs italic text-muted-foreground">
                                AI is typing...
                            </div>
                        </div>
                    )}
                </ScrollArea>
            </CardContent>

            {/* Input */}
            <div className="flex flex-col gap-2 p-2">
                <div className="flex items-end gap-2">
                    <Textarea
                        value={inputMessage}
                        onChange={(e) => setInputMessage(e.target.value)}
                        onKeyDown={handleKeyDown}
                        placeholder="Type your message..."
                        className="resize-none grow overflow-hidden p-2"
                        rows={1}
                    />
                    <Button onClick={handleSend}>Send</Button>
                </div>
            </div>
        </Card>
    );
}

Shadcn Components

Primitives required in your @/components/ui directory.

Component Path
card @/components/ui/card
scroll area @/components/ui/scroll-area
avatar @/components/ui/avatar
button @/components/ui/button
textarea @/components/ui/textarea
select @/components/ui/select
separator @/components/ui/separator

Functional Logic

Internal functions used to handle component logic.

Function Parameters
handleSend() None
handleKeyDown() e: React.KeyboardEvent<HTMLTextAreaElement>
handleClearConversation() None
AIChatWindow01() None

State Management

React state variables managed within this component.

Variable Initial Value
inputMessage ""
selectedModel "chatgpt"
searchEnabled false
isStreaming false

Variables

All variable declarations found in this component.

Name Kind Value
scrollRef const useRef<HTMLDivElement>(null)
newMessage const {
aiResponse const {

JSX Tags Usage

Every JSX/HTML tag used in this component, with usage count and source.

Tag Count Type Source
<Avatar> Library @/components/ui/avatar
<AvatarFallback> Library @/components/ui/avatar
<AvatarImage> Library @/components/ui/avatar
<Card> Library @/components/ui/card
<CardContent> Library @/components/ui/card
<HTMLDivElement> Library Local / Inline
<HTMLTextAreaElement> Library Local / Inline
<ScrollArea> Library @/components/ui/scroll-area
<SelectContent> Library @/components/ui/select
<SelectItem> Library @/components/ui/select
<SelectTrigger> Library @/components/ui/select
<SelectValue> Library @/components/ui/select
<Button> Native Native HTML
<div> Native Native HTML
<h3> Native Native HTML
<Select> Native Native HTML
<Textarea> Native Native HTML

Related Components

  • AI Chat Bubble — A conversational chat bubble component with typing indicator, avatar support, and message timestamps for AI chat interfaces.
  • AI Chat Input Bar — A modern chat input bar with send button, attachment support, and auto-resize textarea for AI conversation interfaces.
  • AI Streaming Text — A typewriter-style streaming text component that simulates token-by-token AI response generation with cursor animation.
  • AI Chat Sidebar — A conversation history sidebar listing previous AI chat sessions with search, date grouping, and active state indicators.
  • AI Model Selector — A dropdown model selector for choosing between AI models (GPT-4, Claude, Gemini) with model info badges and capability indicators.
  • AI Response Actions — An action bar for AI responses with copy, regenerate, thumbs up/down, and share buttons with tooltip labels.