> ## Documentation Index
> Fetch the complete documentation index at: https://osmium.intface.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Chat

> A complete chat interface with message list and input, featuring smart auto-scroll management and optimized UX for AI conversations.

The Chat component is the core of Osmium, providing a complete chat interface optimized for AI conversations. It combines a message list and message input with intelligent scroll management and UX features similar to ChatGPT and Grok.

## Installation

<Tabs>
  <Tab title="CLI">
    Install the Chat component using the shadcn CLI:

    <CodeGroup>
      ```bash pnpm  theme={null}
      pnpm dlx shadcn@latest add https://registry.intface.io/chat.json
      ```

      ```bash npm  theme={null}
      npx shadcn@latest add https://registry.intface.io/chat.json
      ```

      ```bash yarn  theme={null}
      yarn shadcn@latest add https://registry.intface.io/chat.json
      ```

      ```bash bun  theme={null}
      bunx --bun shadcn@latest add https://registry.intface.io/chat.json
      ```
    </CodeGroup>
  </Tab>

  <Tab title="Manual">
    ```tsx Chat.tsx expandable theme={null}
    "use client";
    import { MessageList } from "./message-list";
    import { MessageInput } from "./message-input";
    import type { UIMessage } from "@ai-sdk/react";
    import type { ChatStatus } from "ai";
    import { useEffect, useRef, useState } from "react";

    type ChatProps = {
      status: ChatStatus;
      messages: UIMessage[];
      onSend: (message: string) => void;
      onStop: () => void;
    };

    const Chat = ({ status, messages, onSend, onStop }: ChatProps) => {
      const messageListRef = useRef<HTMLDivElement>(null);
      const chatContainerRef = useRef<HTMLDivElement>(null);
      const lastUserMessageRef = useRef<HTMLDivElement>(null);
      const lastAssistantMessageRef = useRef<HTMLDivElement>(null);
      const bottomSentinelRef = useRef<HTMLDivElement>(null);
      const [lastUserMessageIndex, setLastUserMessageIndex] = useState(0);
      const [isAtBottom, setIsAtBottom] = useState(true);
      const [lastMessageRole, setLastMessageRole] = useState<
        "user" | "assistant"
      >();

      const handleSendMessage = (message: string) => {
        lastUserMessageRef.current = null;
        onSend(message);
      };

      const scrollToBottom = () => {
        setIsAtBottom(true);
        messageListRef.current?.scrollTo({
          top: messageListRef.current?.scrollHeight,
          behavior: "smooth",
        });
      };

      useEffect(() => {
        setLastUserMessageIndex(messages.findLastIndex((m) => m.role === "user"));
        setLastMessageRole(
          messages[messages.length - 1]?.role as "user" | "assistant"
        );
      }, [messages]);

      useEffect(() => {
        if (lastUserMessageRef.current) {
          if (lastUserMessageIndex === 0) {
            scrollToBottom();
            return;
          }
          const scrollMarginTop =
            lastUserMessageRef.current.clientHeight < 128
              ? 24
              : -(lastUserMessageRef.current.clientHeight - 64);
          lastUserMessageRef.current.style.scrollMarginTop = `${scrollMarginTop}px`;

          lastUserMessageRef.current.scrollIntoView({
            behavior: "smooth",
          });
        }
      }, [lastUserMessageIndex]);

      useEffect(() => {
        const messageListObserver = new IntersectionObserver(
          (entries) => {
            const entry = entries[0];
            setIsAtBottom(entry.isIntersecting);
          },
          {
            root: messageListRef.current,
            threshold: 0,
          }
        );

        if (bottomSentinelRef.current) {
          messageListObserver.observe(bottomSentinelRef.current);
        }
        return () => {
          messageListObserver.disconnect();
        };
      }, [bottomSentinelRef.current]);

      return (
        <div
          className="h-[90vh] w-full overflow-hidden relative"
          ref={chatContainerRef}
        >
          <div className="h-full">
            <MessageList
              status={status}
              ref={messageListRef}
              messages={messages}
              chatContainerHeight={chatContainerRef.current?.clientHeight}
              lastUserMessageRef={lastUserMessageRef}
              lastAssistantMessageRef={lastAssistantMessageRef}
              bottomSentinelRef={bottomSentinelRef}
              scrollToBottom={scrollToBottom}
              isAtBottom={isAtBottom}
              lastMessageRole={lastMessageRole}
            />
          </div>
          <MessageInput
            status={status}
            className="sticky bottom-4 left-0 right-0"
            onSendMessage={handleSendMessage}
            onStop={onStop}
          />
        </div>
      );
    };

    export default Chat;

    ```
  </Tab>
</Tabs>

## Usage

```tsx theme={null}
import Chat from "@/components/chat";
import { useChat } from "ai/react";

export default function App() {
  const { messages: chatMessages, sendMessage, status, stop } = useChat();

  const handleSend = async (message: string) => {
    sendMessage({ text: message });
  };

  const handleStop = () => {
    stop();
  };

  return (
    <div className="h-screen flex flex-col items-center justify-center">
      <Chat
        status={status}
        messages={chatMessages}
        onSend={handleSend}
        onStop={handleStop}
      />
    </div>
  );
}
```

## Props

<ParamField body="status" type="ChatStatus" required>
  The current status of the chat. Controls the input state and loading
  indicators.
</ParamField>

<ParamField body="messages" type="UIMessage[]" required>
  Array of chat messages to display. Uses the standard UI message format from AI
  SDK.
</ParamField>

<ParamField body="onSend" type="(message: string) => void" required>
  Callback function called when the user sends a message. Receives the message
  content as a string.
</ParamField>

<ParamField body="onStop" type="() => void" required>
  Callback function called when the user wants to stop the AI response
  generation.
</ParamField>
