Self-contained, portable AI chat component. Zero external dependencies beyond react, clsx, and lucide-react.
import { ChatApp, createHttpConnector } from "./chat-app";
// Point at your backend — one line
<ChatApp config={{
agentConnector: createHttpConnector("/api/chat"),
logo: "/my-logo.png",
title: "My Assistant",
}} />Or use the mock for development without a backend:
import { ChatApp, MockAgentConnector } from "./chat-app";
<ChatApp config={{
agentConnector: MockAgentConnector,
logo: "/my-logo.png",
title: "My Assistant",
}} />One import, one component, one config object.
<ChatApp config={...}>
└── ChatThemeProvider → scoped --chat-* CSS variables
└── ChatContext → hooks: fingerprint, conversations, agent connection
├── ChatBubble → floating trigger button (bottom-right or bottom-left)
└── ChatWindow → the chat panel
├── ChatHeader → logo, title, status, controls
├── MessageList → scrollable messages + typing indicator
│ └── MessageBubble → single message (user or assistant)
├── ConversationList → history sidebar
└── MessageInput → textarea + send button
Every user gets a unique fingerprint that identifies their agent session:
config.fingerprint→ use directly (e.g., Cognito JWTsub)localStorage["chat-app:fingerprint"]→ use stored UUIDcrypto.randomUUID()→ generate, store, use
The fingerprint flows through every sendMessage() call. The backend uses it as the agent session ID, DynamoDB partition key, or S3 prefix — whatever makes sense for the product.
Conversations persist to localStorage keyed by fingerprint: chat-app:conversations:{fingerprint}. Different users on the same browser get separate history.
interface ChatAppConfig {
// REQUIRED — how the module talks to the AI backend
agentConnector: AgentConnector;
// Branding
logo?: string | ReactNode; // URL string, React element, or omit for default Bot icon
title?: string; // Header text (default: none)
placeholder?: string; // Input placeholder (default: "Message...")
welcomeMessage?: string; // First message in new conversations
// Per-user identity
fingerprint?: string; // Override auto-generated UUID (e.g., Cognito sub)
// Theme
theme?: Partial<ChatTheme>; // Override any of the 14 color tokens
// Layout
position?: "bottom-right" | "bottom-left"; // Bubble position (default: bottom-right)
zIndex?: number; // Stacking order (default: 30)
}See docs/connectors.md for the full guide.
See docs/theming.md for the full guide.
See docs/architecture.md for the component hierarchy, data flow, and extension points.
chat-app/
├── index.ts # Public exports
├── ChatApp.tsx # Root component
├── ChatContext.ts # Internal React context
├── types/
│ ├── index.ts # Re-exports
│ ├── message.ts # Message, Conversation
│ ├── connector.ts # AgentConnector, AgentResponse
│ └── config.ts # ChatAppConfig, ChatTheme
├── connector/
│ ├── HttpConnector.ts # Generic HTTP connector factory (createHttpConnector)
│ └── MockAgentConnector.ts # Simulated responses for demo mode
├── hooks/
│ ├── useFingerprint.ts # Per-user identity
│ ├── useConversations.ts # CRUD + localStorage persistence
│ └── useAgentConnection.ts # Wraps connector.sendMessage()
├── components/
│ ├── ChatBubble.tsx # Floating trigger button
│ ├── ChatWindow.tsx # Panel frame + sizing
│ ├── ChatHeader.tsx # Title bar + controls
│ ├── MessageList.tsx # Message feed + typing indicator
│ ├── MessageBubble.tsx # Single message row
│ ├── MessageInput.tsx # Text input + send
│ └── ConversationList.tsx # History sidebar
├── theme/
│ ├── defaults.ts # Default color tokens
│ └── ChatThemeProvider.tsx # Scoped CSS variable injection
└── docs/
├── architecture.md # Component hierarchy + data flow
├── connectors.md # How to create connectors
└── theming.md # How to customize colors
// Components
export { ChatApp } from "./ChatApp";
// Connectors
export { MockAgentConnector } from "./connector/MockAgentConnector";
export { createHttpConnector } from "./connector/HttpConnector";
// Types (for host app to implement)
export type { ChatAppConfig, ChatTheme } from "./types";
export type { AgentConnector, AgentResponse } from "./types";
export type { HttpConnectorOptions } from "./connector/HttpConnector";
export type { Message, Conversation } from "./types";- Never import host app code — no
@/components/,@/lib/,@/hooks/ - Never use global CSS vars — only
--chat-*from ChatThemeProvider - Never hardcode branding — logo, title, colors come from config
- All state is self-contained — localStorage keyed by fingerprint