AIChat
The AIChat container organizes visual components (AIMessages, AIChatInput, AIThinkingIndicator, and AISuggestedActions) into a responsive layout. It manages message logs, handles thinking states, displays suggested actions when empty, and provides customizable slots for custom headers, sidebars, attachments, and footer captions.
- Preview
- Code
History
Ignix AssistantReady for instructions
✦
How can I help you today?
Select a prompt starter suggestion below or enter a prompt inside the input message bar.
Code sync hookWrite router-synchronized state React hook.
SaaS growth ideasBrainstorm creative marketing growth loops.
AI answers may contain errors. Please verify component configurations.
import { AIChat, AIModelSelector } from '@mindfiredigital/ignix-ui';
import { useState } from 'react';
function SandboxChat() {
const [messages, setMessages] = useState([]);
const [inputValue, setInputValue] = useState("");
const [modelId, setModelId] = useState("gpt-4o");
const handleSend = (text) => {
setMessages(prev => [...prev, { id: Date.now().toString(), role: "user", content: text }]);
// Trigger assistant reply logic...
};
return (
<AIChat
messages={messages}
inputValue={inputValue}
onInputChange={setInputValue}
onSend={handleSend}
variant="default"
headerSlot={
<div className="flex justify-between items-center w-full">
<span>Copilot Workspace</span>
<AIModelSelector
selectedModelId={modelId}
onModelChange={(m) => setModelId(m.id)}
size="sm"
/>
</div>
}
/>
);
}
Installation
- CLI
- Manual
ignix add component ai-chat
"use client";
import * as React from "react";
import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "../../../utils/cn";
import { AIMessages, type AIMessagesItem } from "../ai-messages";
import { AIChatInput } from "../ai-chat-input";
import { AISuggestedActions, type SuggestedAction } from "../ai-suggested-actions";
const chatContainerVariants = cva(
"flex flex-col w-full h-[600px] border rounded-2xl overflow-hidden relative shadow-sm transition-all duration-300",
{
variants: {
variant: {
default:
"bg-card border-border text-card-foreground shadow-md",
dark:
"bg-[var(--color-dark-dropdown-bg)] border-[var(--color-dark-dropdown-border)] text-[var(--color-dark-dropdown-text)] shadow-lg",
glass:
"bg-[var(--color-glass-bg)] border border-[var(--color-glass-border)] backdrop-blur-xl backdrop-saturate-150 text-[var(--color-glass-text)] shadow-[var(--color-glass-shadow)]",
minimal:
"bg-transparent !border-transparent text-foreground shadow-none",
}
},
defaultVariants: {
variant: "default",
},
}
);
export interface AIChatProps extends VariantProps<typeof chatContainerVariants> {
// --- Message Feed Props ---
messages: AIMessagesItem[];
isThinking?: boolean;
emptyState?: React.ReactNode;
autoScroll?: boolean;
showJumpToBottom?: boolean;
// --- Chat Input Props ---
inputValue: string;
onInputChange: (value: string) => void;
onSend: (value: string) => void;
onStop?: () => void;
isStreaming?: boolean;
inputPlaceholder?: string;
disabled?: boolean;
// --- Suggested Actions Props ---
suggestedActions?: SuggestedAction[];
onSuggestedActionClick?: (actionText: string) => void;
suggestedActionsLayout?: "flex" | "grid";
// --- Custom Slots ---
headerSlot?: React.ReactNode;
footerSlot?: React.ReactNode;
attachmentSlot?: React.ReactNode;
// --- Styling ---
className?: string;
messagesClassName?: string;
inputClassName?: string;
}
export const AIChat = React.forwardRef<HTMLDivElement, AIChatProps>(
(
{
messages = [],
isThinking = false,
emptyState,
autoScroll = true,
showJumpToBottom = true,
inputValue,
onInputChange,
onSend,
onStop,
isStreaming = false,
inputPlaceholder = "Message...",
disabled = false,
suggestedActions = [],
onSuggestedActionClick,
suggestedActionsLayout = "grid",
headerSlot,
footerSlot,
attachmentSlot,
variant = "default",
className,
messagesClassName,
inputClassName,
...props
},
ref
) => {
const handleSuggestedActionClick = (actionText: string) => {
if (onSuggestedActionClick) {
onSuggestedActionClick(actionText);
} else {
onInputChange(actionText);
}
};
return (
<div
ref={ref}
className={cn(chatContainerVariants({ variant }), className)}
{...props}
>
<div className="flex flex-col h-full w-full">
{/* Header Slot */}
{headerSlot && (
<div
className={cn(
"border-b p-3.5 flex items-center justify-between shrink-0",
variant === "glass"
? "border-white/10 text-white"
: "border-border"
)}
>
{headerSlot}
</div>
)}
{/* Main Body Section */}
<div className="flex flex-1 min-h-0 relative overflow-hidden">
<div className="flex flex-col flex-1 min-w-0 relative">
{messages.length > 0 ? (
<AIMessages
messages={messages}
isThinking={isThinking}
emptyState={emptyState}
autoScroll={autoScroll}
showJumpToBottom={showJumpToBottom}
variant={variant}
className={cn("flex-1 p-4", messagesClassName)}
/>
) : (
<div className="flex-1 flex flex-col items-center justify-center p-6 text-center space-y-6 overflow-y-auto">
{emptyState ? (
emptyState
) : (
<div className="space-y-2 max-w-sm">
<div className={cn(
"w-12 h-12 rounded-2xl flex items-center justify-center mx-auto text-xl font-bold",
variant === "glass"
? "bg-white/10 border border-white/20 text-white shadow-lg"
: "bg-primary/10 text-primary")}
>
✦
</div>
<h3
className={cn(
"text-sm font-bold tracking-tight",
variant === "glass" || variant === "dark"
? "text-white"
: "text-card-foreground"
)}
>How can I help you today?</h3>
<p className={cn(
"text-xs leading-relaxed max-w-xs mx-auto",
variant === "glass" || variant === "dark"
? "text-white/70"
: "text-muted-foreground")}
>
Select a prompt starter suggestion below or enter a prompt inside the input message bar.
</p>
</div>
)}
{suggestedActions && suggestedActions.length > 0 && (
<div className="w-full max-w-md mx-auto pt-4">
<AISuggestedActions
actions={suggestedActions}
layout={suggestedActionsLayout}
variant={variant}
onActionClick={handleSuggestedActionClick}
/>
</div>
)}
</div>
)}
</div>
{/* Sidebar Slot */}
</div>
{/* Footer Area Section */}
<div className={cn(
"p-4 border-t shrink-0 space-y-2",
variant === "glass"
? "border-[var(--color-glass-border)]"
: "border-border")}
>
<AIChatInput
value={inputValue}
onChange={onInputChange}
onSend={onSend}
onStop={onStop}
isStreaming={isStreaming}
placeholder={inputPlaceholder}
disabled={disabled}
attachmentSlot={attachmentSlot}
variant={variant}
className={inputClassName}
/>
{footerSlot && (
<div className={cn(
"text-[10px] text-center font-medium",
variant === "glass"
? "text-white/60"
: "text-muted-foreground")}
>
{footerSlot}
</div>
)}
</div>
</div>
</div>
);
}
);
AIChat.displayName = "AIChat";
Usage
Import the component:
import { AIChat } from '@mindfiredigital/ignix-ui';
Basic Usage
import { AIChat } from '@mindfiredigital/ignix-ui';
import { useState } from 'react';
function SimpleAssistant() {
const [messages, setMessages] = useState([
{ id: '1', role: 'assistant', content: 'Hello! Ask me anything.' }
]);
const [input, setInput] = useState('');
const handleSend = (text: string) => {
setMessages(prev => [...prev, { id: Date.now().toString(), role: 'user', content: text }]);
setInput('');
};
return (
<AIChat
messages={messages}
inputValue={input}
onInputChange={setInput}
onSend={handleSend}
/>
);
}
Slot Configurations
Utilize the custom slot parameters to append side controls, header titles, and footer legal info:
import { AIChat } from './components/ui';
import { Bot, Settings } from 'lucide-react';
function CustomWorkspace() {
return (
<AIChat
messages={messages}
inputValue={input}
onInputChange={setInput}
onSend={handleSend}
// Custom Header Slot
headerSlot={
<div className="flex justify-between items-center w-full">
<div className="flex items-center gap-2">
<Bot className="text-indigo-500" />
<span className="font-bold text-sm">Workspace Copilot</span>
</div>
<Settings size={16} className="cursor-pointer text-neutral-400" />
</div>
}
// Custom Footer Info
footerSlot="Copilot answers may contain inaccuracies. Please double check vital outputs."
/>
);
}
Props
| Parameter | Type | Default | Description |
|---|---|---|---|
messages | AIMessagesItem[] | [] | Message list logs to render in the scrollable viewport. |
isThinking | boolean | false | Renders a loading ellipsis thinking indicator below messages. |
inputValue | string | — | Text content value of the chat textarea. |
onInputChange | (value: string) => void | — | Callback fired when the input textarea changes. |
onSend | (value: string) => void | — | Callback fired when input text query is sent/submitted. |
isStreaming | boolean | false | Sets a streaming state, changing send button into a stop trigger. |
suggestedActions | SuggestedAction[] | [] | Prompt template actions displayed when no messages are logged. |
variant | 'default' | 'dark' | 'glass' | 'minimal' | 'default' | Theme variants. |
headerSlot | ReactNode | — | Appended slot to render customizable headers. |
footerSlot | ReactNode | — | Captions or copy subtext below the input bar. |
attachmentSlot | ReactNode | — | Component placement slot inside the input textarea. |