Copy-paste chat UI components for React. Own your code.
Inspired by shadcn/ui - this is NOT a component library you install as a dependency. Instead, you copy the source code into your project and customize it however you want.
- You own the code - No npm dependency, no version conflicts
- Full customization - Change anything you want
- Learn by reading - The code is right there
- No lock-in - It's just React + Tailwind
npx @connectonion/chat-ui addThis copies 10 files to ./components/chat/:
chat.tsx- Main chat component with empty statechat-message.tsx- Single message with avatarchat-messages.tsx- Scrollable message listchat-input.tsx- Auto-resizing textarea inputchat-typing.tsx- Typing indicator animationchat-empty-state.tsx- Welcome screen with suggestionsuse-chat.ts- State management hooktypes.ts- TypeScript typesutils.ts-cn()utility functionindex.ts- Exports
npm install clsx tailwind-merge'use client'
import { Chat, useChat } from '@/components/chat'
export default function ChatPage() {
const { messages, isLoading, send } = useChat({
onSend: async (message) => {
const response = await fetch('/api/chat', {
method: 'POST',
body: JSON.stringify({ message }),
})
const data = await response.json()
return data.reply
},
})
return (
<main className="h-screen">
<Chat
messages={messages}
onSend={send}
isLoading={isLoading}
/>
</main>
)
}The all-in-one chat component. Shows empty state when no messages, typing indicator when loading.
<Chat
messages={messages} // Message[]
onSend={send} // (message: string) => void
isLoading={isLoading} // Shows typing indicator
placeholder="Send a message..." // Input placeholder
emptyStateTitle="How can I help?" // Empty state heading
emptyStateDescription="Ask anything" // Empty state description
suggestions={['Hello', 'Help me']} // Suggestion buttons
/>Welcome screen shown when there are no messages.
<ChatEmptyState
title="How can I help you?"
description="Ask me anything"
suggestions={['Write code', 'Debug', 'Explain']}
onSuggestionClick={(text) => send(text)}
/>A single message with avatar and role label.
<ChatMessage
message={{
id: '1',
role: 'assistant',
content: 'Hello! How can I help?',
}}
/>Auto-resizing textarea with send button.
<ChatInput
onSend={(message) => console.log(message)}
isLoading={false}
placeholder="Type a message..."
hint="Press Enter to send"
/>Animated typing indicator.
{isLoading && <ChatTyping />}State management for chat.
const {
messages, // Message[]
isLoading, // boolean
send, // (content: string) => Promise<void>
addMessage, // (message: Omit<Message, 'id'>) => void
setMessages, // React setState
clear, // () => void
} = useChat({
initialMessages: [],
onSend: async (message, messages) => {
// Return the assistant's response
return 'Hello!'
},
})interface Message {
id: string
role: 'user' | 'assistant' | 'system'
content: string
createdAt?: Date
}Components use Tailwind CSS. Edit the files directly:
// components/chat/chat-message.tsx
// Change avatar gradient
<div className="bg-gradient-to-br from-blue-500 to-cyan-500">Compose the primitives however you want:
<div className="flex h-screen">
<Sidebar />
<main className="flex-1">
<Header />
<ChatMessages messages={messages} />
<ChatInput onSend={send} />
</main>
</div>The code is yours. Add whatever you need:
- Markdown rendering
- Code syntax highlighting
- File uploads
- Message actions (copy, regenerate)
- Streaming responses
npx @connectonion/chat-ui add # Add to ./components/chat
npx @connectonion/chat-ui add --path ./src/chat # Custom path
npx @connectonion/chat-ui add --force # Overwrite existing
npx @connectonion/chat-ui list # List components
npx @connectonion/chat-ui help # Show help- React 18+
- Tailwind CSS 3+
- Node.js 16+
This project follows the shadcn/ui philosophy:
"Copy and paste components directly into your app and customize them. You decide how things are built."
Chat interfaces are highly customized per application. Instead of fighting with a library's API, just own the code and modify it.
MIT