LLM Chat

PreviousNext

A complete chat component with streaming responses, file uploads, and model selection for building AI-powered applications.

Installation

pnpm dlx shadcn@latest add https://deltacomponents.dev/r/chat.json

Usage

import { Chat } from "@/components/chat"
<Chat onMessage={handleMessage} allowFileUpload={true} />

Examples

Chat with Previous Messages

Can you explain how to use React hooks effectively?

React Hooks Best Practices

React hooks are a powerful feature that let you use state and other React features without writing classes. Here are some tips for using them effectively:

Rules of Hooks

  1. Only call hooks at the top level of your component or custom hooks
  2. Don't call hooks inside loops, conditions, or nested functions

Common Hooks

  • useState: For local component state
  • useEffect: For side effects like data fetching
  • useContext: For consuming context
  • useReducer: For complex state logic
  • useCallback: For memoizing functions
  • useMemo: For memoizing values

Example of useState and useEffect

jsx

Would you like me to explain any specific hook in more detail?

Yes, could you explain useCallback and useMemo in more detail? When should I use one over the other?

The user is asking for a detailed explanation of useCallback and useMemo. I should provide a clear and concise explanation of each hook's purpose and how they differ.

The useCallback hook is used to memoize functions to prevent unnecessary re-renders of child components that receive functions as props.

The useMemo hook is used to memoize values to avoid expensive recalculations on every render.

Both hooks help with performance optimization, but they serve different purposes.

useCallback vs useMemo

Both hooks help with performance optimization, but they serve different purposes:

useCallback

useCallback memoizes functions to prevent unnecessary re-renders of child components that receive functions as props.

jsx

useMemo

useMemo memoizes values to avoid expensive recalculations on every render.

jsx

When to use which?

  • Use useCallback when:

    • Passing callbacks to optimized child components that rely on reference equality
    • Working with event handlers that you pass to child components
  • Use useMemo when:

    • You have computationally expensive calculations
    • You want to avoid recreating objects that are used as dependencies for other hooks

Performance Note

Don't overuse these hooks! They come with their own overhead. Only use them when you have identified a genuine performance issue.

Non-Streaming Chat

For use cases where LLM streaming doesn't happen and you receive the whole message in one go. Shows a loader until the message is received and then displays the complete message.

Chat with Shimmer Effect

Demonstrates the use of the shimmer component for loading states while AI processes requests.

Chat with Reasoning

Shows how to integrate the reasoning component to display AI thinking processes before responses.

API Reference

Chat

PropTypeDefaultDescription
allowFileUploadbooleanfalseEnable file upload functionality
userAvatarstring-Custom user avatar URL
modelsModel[][{id: 'gpt-4', name: 'GPT-4', model: 'GPT-4o'}, {id: 'claude', name: 'Claude', model: 'Claude 3.5'}]Available AI models
onMessage(message: string, modelId: string) => Promise<string>-Message handler function
childrenReactNode-Custom content in chat options area
classNamestring-Additional CSS classes

Individual Components

The chat component is composed of several modular components that can be used independently:

  • ChatMessages - Message display container
  • ChatUserMessage - User message bubble
  • ChatAssistantMessage - AI response message bubble
  • ChatPromptInput - Input area with model selection
  • ChatResponse - Streamdown markdown renderer
  • ChatStreamingResponse - Streaming text effect
  • ChatReasoning - "Thinking..." shimmer animation
  • ChatSendButton - Standalone send button component
  • ChatModelSelector - Model selection dropdown
  • ChatOptions - Container for custom content before input

ChatSendButton

PropTypeDefaultDescription
isDisabledboolean-Whether the button is disabled
isLoadingboolean-Loading state indicator
classNamestring-Custom CSS classes

ChatModelSelector

PropTypeDefaultDescription
selectedModelstring-Currently selected model ID
onModelChange(modelId: string) => void-Model selection callback
modelsModel[]-Available models array
classNamestring-Custom CSS classes

ChatOptions

PropTypeDefaultDescription
childrenReactNode-Content to display in options area
classNamestring-Custom CSS classes

Styling with Data Attributes

The chat component uses data attributes for easy CSS targeting without prop drilling:

Data AttributeTarget ElementDescription
[data-chat="send-button"]Send buttonStyle the submit button
[data-chat="model-selector"]Model selector dropdownStyle the model selection button
[data-chat="options"]Options containerStyle the options area
[data-chat="prompt-input"]Prompt input containerStyle the entire input area
[data-chat="textarea"]Textarea elementStyle the text input field

Example CSS

/* Custom send button styling */
[data-chat="send-button"] {
  background: linear-gradient(45deg, #ff6b6b, #ee5a52);
  border-radius: 50%;
}
 
/* Custom textarea styling */
[data-chat="textarea"] {
  background-color: #f8f9fa;
  border: 2px solid #e9ecef;
  font-family: "Inter", sans-serif;
}
 
/* Custom model selector styling */
[data-chat="model-selector"] {
  background-color: #495057;
  color: white;
  border-radius: 8px;
}
 
/* Custom options area */
[data-chat="options"] {
  padding: 16px;
  background-color: #f1f3f4;
  border-radius: 12px;
}

Features

  • Streaming Responses: Real-time text streaming with typewriter effect
  • File Upload: Drag & drop or click to upload files with preview
  • Model Selection: Dropdown to choose between different AI models
  • Rich Markdown: Full support for code blocks, tables, math, and more
  • Responsive Design: Works on desktop and mobile devices
  • Customizable: Modular components for flexible integration
  • TypeScript: Full type safety and IntelliSense support

Enhanced Scrollbar UI/UX

For a better scrollbar experience in your chat interface, add the thin-scrollbar utility class to your global CSS:

@utility thin-scrollbar {
  scrollbar-width: thin;
  scrollbar-color: rgb(163 163 163) transparent;
 
  &::-webkit-scrollbar {
    width: 2px;
    height: 2px;
  }
 
  &::-webkit-scrollbar-track {
    background: transparent;
  }
 
  &::-webkit-scrollbar-thumb {
    background-color: rgb(163 163 163);
    border-radius: 2px;
  }
 
  &::-webkit-scrollbar-thumb:hover {
    background-color: rgb(115 115 115);
  }
 
  @variant dark {
    scrollbar-color: rgb(82 82 82) transparent;
 
    &::-webkit-scrollbar-thumb {
      background-color: rgb(82 82 82);
    }
 
    &::-webkit-scrollbar-thumb:hover {
      background-color: rgb(115 115 115);
    }
  }
}

This utility provides:

  • Ultra-thin 2px scrollbars for a minimal, modern look
  • Neutral gray colors that work well with any theme
  • Dark mode support with appropriate contrast
  • Smooth hover interactions
  • Cross-browser compatibility

The ChatContainer component automatically applies this styling when the utility is available.

Examples

Custom Message Handler

const handleMessage = async (message: string, modelId: string) => {
  const response = await fetch("/api/chat", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ message, modelId }),
  })
 
  return response.text()
}
 
;<Chat onMessage={handleMessage} />

With Custom Models

const customModels = [
  { id: 'gpt-4', name: 'GPT-4', model: 'GPT-4o' },
  { id: 'claude', name: 'Claude', model: 'Claude 3.5 Sonnet' },
  { id: 'gemini', name: 'Gemini', model: 'Gemini Pro' }
]
 
<Chat models={customModels} />

With File Upload

<Chat allowFileUpload={true} onMessage={handleMessage} />

Custom Styling with CSS

// Add custom styles in your CSS file
<Chat className="my-custom-chat">
  <div className="flex items-center gap-2">
    <span>Custom controls here</span>
  </div>
</Chat>
/* Custom styling using data attributes */
.my-custom-chat [data-chat="send-button"] {
  background-color: #ef4444;
  transition: background-color 0.2s;
}
 
.my-custom-chat [data-chat="send-button"]:hover {
  background-color: #dc2626;
}
 
.my-custom-chat [data-chat="textarea"] {
  background-color: #f3f4f6;
  border-color: #d1d5db;
}
 
.my-custom-chat [data-chat="model-selector"] {
  color: #2563eb;
  border-color: #93c5fd;
}

Using ChatOptions

<Chat>
  <div className="bg-muted flex items-center justify-between rounded-md p-2">
    <span className="text-muted-foreground text-sm">AI Assistant</span>
    <button className="text-primary text-xs">Clear Chat</button>
  </div>
</Chat>

Individual Components

import {
  ChatAssistantMessage,
  ChatMessages,
  ChatModelSelector,
  ChatOptions,
  ChatPromptInput,
  ChatSendButton,
  ChatUserMessage,
} from "@/components/chat"
 
// Use components independently
 
;<div className="flex h-full flex-col">
  <ChatMessages messages={messages} isLoading={loading} />
  <ChatPromptInput
    input={input}
    onInputChange={setInput}
    onSubmit={handleSubmit}
    // ... other props
  />
</div>
 
// Or use individual components for custom layouts
;<div className="flex items-center gap-2">
  <ChatModelSelector
    selectedModel={model}
    onModelChange={setModel}
    models={models}
    className="rounded-md border"
  />
  <ChatSendButton
    isDisabled={!input.trim()}
    isLoading={loading}
    className="bg-green-500"
  />
</div>