ChatWindow

PreviousNext

A fully controlled chatbot panel with a header, scrollable message list, typing indicator, and input bar.

Ask me a question

Hi. Ask me anything about Club Med. I'm here to help you find the right information quickly.

My son is 14 years old, and I would like to find activities for him during our stay.

'use client';

import { useState } from 'react';
import { ChatWindow } from '@/ui/ChatWindow';
import type { ChatWindowMessage } from '@/ui/ChatWindow';

export function ChatWindowDemo() {
  const [messages, setMessages] = useState<ChatWindowMessage[]>([
    {
      id: '1',
      sender: 'assistant',
      content:
        "Hi. Ask me anything about Club Med. I'm here to help you find the right information quickly.",
    },
    {
      id: '2',
      sender: 'user',
      content:
        'My son is 14 years old, and I would like to find activities for him during our stay.',
    },
  ]);
  const [inputValue, setInputValue] = useState('');
  const [isTyping, setIsTyping] = useState(false);

  function handleSubmit() {
    if (!inputValue.trim()) return;
    const userMsg: ChatWindowMessage = {
      id: Date.now().toString(),
      sender: 'user',
      content: inputValue,
    };
    setMessages((prev) => [...prev, userMsg]);
    setInputValue('');
    setIsTyping(true);
    setTimeout(() => {
      setIsTyping(false);
      setMessages((prev) => [
        ...prev,
        {
          id: (Date.now() + 1).toString(),
          sender: 'assistant',
          content: 'Let me look into that for you…',
        },
      ]);
    }, 2000);
  }

  return (
    <ChatWindow
      messages={messages}
      inputValue={inputValue}
      isTyping={isTyping}
      onInputChange={setInputValue}
      onSubmit={handleSubmit}
      onClose={() => {}}
      onAudioClick={() => {}}
      className="w-380 h-660"
    />
  );
}

Installation

npx shadcn@latest add https://develop.trident-ui.pro.clubmed/r/chat-window.json

Usage

import { ChatWindow } from '@/ui/ChatWindow';
import type { ChatWindowMessage } from '@/ui/ChatWindow';
import { useState } from 'react';
 
export function Component() {
  const [messages, setMessages] = useState<ChatWindowMessage[]>([]);
  const [inputValue, setInputValue] = useState('');
  const [isTyping, setIsTyping] = useState(false);
 
  function handleSubmit() {
    if (!inputValue.trim()) return;
    setMessages((prev) => [
      ...prev,
      { id: Date.now().toString(), sender: 'user', content: inputValue },
    ]);
    setInputValue('');
    // Trigger your AI call here, then set isTyping accordingly
  }
 
  return (
    <ChatWindow
      messages={messages}
      inputValue={inputValue}
      isTyping={isTyping}
      onInputChange={setInputValue}
      onSubmit={handleSubmit}
      onClose={() => console.log('close')}
      onAudioClick={() => console.log('audio')}
    />
  );
}

API Reference

ChatWindow

PropTypeDefaultDescription
messagesChatWindowMessage[]Required. Ordered list of messages to display
inputValuestringRequired. Controlled value of the text input
onInputChange(value: string) => voidRequired. Called on every keystroke in the input
onSubmit() => voidRequired. Called when the user submits a message
titlestring"Ask me a question"Heading displayed in the intro header
greetingstringundefinedOptional static greeting text shown above the message list
inputPlaceholderstring"Ask a question?"Placeholder text for the input field
isTypingbooleanfalseWhen true, shows the animated typing indicator
onClose() => voidundefinedWhen provided, renders a close button below the panel
onAudioClick() => voidundefinedWhen provided, renders an audio/attachment button in the input
logoReactNodeundefinedCustom logo node; defaults to a Sparkles icon button
classNamestringundefinedAdditional CSS classes on the outer wrapper

ChatWindowMessage

interface ChatWindowMessage {
  id: string;
  sender: 'assistant' | 'user';
  content: string;
}

Notes

  • The component is entirely controlled from the outside — all state (messages, input value, typing indicator) is managed by the parent.
  • The message list uses role="log" with aria-live="polite" so screen readers announce new messages automatically.
  • isTyping should be set to true while waiting for the AI response, then false when the response arrives.
  • Built from sub-components: ChatMessage, ChatTypingIndicator, and ChatInput.