The library is built on Atomic Design principles, which ensure:
- Clear component hierarchy
- Element reusability
- Easy maintenance
- Flexible customization
Minimal indivisible UI elements without business logic.
Characteristics:
- Do not contain business logic
- Stateless
- Unaware of usage context
- Accept data only through props
Examples: ActionButton, Alert, Loader, Shimmer, MarkdownRenderer
Simple groups of atoms forming basic UI blocks.
Characteristics:
- Combine multiple atoms
- Minimal internal logic
- Reusable blocks
Examples: ButtonGroup, Tabs, Suggestions, BaseMessage, PromptInputBody
Complex self-sufficient components with internal logic.
Characteristics:
- Contain business logic
- Have internal state
- Can be used independently
Examples: Header, MessageList, PromptInput, UserMessage, AssistantMessage, ToolMessage, ThinkingMessage, FileUploadDialog
Complete layouts combining organisms.
Characteristics:
- Define page structure
- Coordinate organism interactions
- Work with abstract data
Examples: History, EmptyContainer, ChatContent
Full integrations with specific data.
Characteristics:
- Highest level of abstraction
- Manage data and state
- Connect all components together
Examples: ChatContainer, AIStudioChat
Code that runs exclusively on the server side and is responsible for interacting with external APIs, primarily neural network services.
Characteristics:
- Runs only on the server
- Contains no UI and is not involved in component rendering
- Serves as a middleware layer between the application and external services (such as neural network APIs)
Examples: OpenAIService (@gravity-ui/aikit/server/openai)
The library offers two ways to integrate a chat:
ChatContainer (or AIStudioChat) — a fully assembled chat with header, history, message list, and prompt input:
import {ChatContainer} from '@gravity-ui/aikit';
<ChatContainer
chats={chats}
activeChat={activeChat}
messages={messages}
onSendMessage={handleSend}
onSelectChat={setActiveChat}
onCreateChat={createChat}
onDeleteChat={deleteChat}
/>;Drop down to individual organisms when you need a custom layout:
import {Header, MessageList, PromptInput} from '@gravity-ui/aikit';
function CustomChat() {
return (
<div className="custom-chat">
<Header title="AI Assistant" onNewChat={createChat} />
<MessageList messages={messages} status="ready" />
<PromptInput onSubmit={handleSend} placeholder="Enter message…" />
</div>
);
}Composition-level hooks (useToolMessage, useSmartScroll, useFileUploadStore, etc.) are documented in HOOKS.md.
- The library is state-agnostic: it does not own messages, chats, or upload state
- All data flows through props
- The library invokes callbacks (
onSendMessage,onSelectChat,onCreateChat,onDeleteChat, …) and lets the host application manage requests, persistence, and errors
const [messages, setMessages] = useState<TChatMessage[]>([]);
const [status, setStatus] = useState<ChatStatus>('ready');
<ChatContainer
messages={messages}
status={status}
onSendMessage={async (data) => {
setStatus('streaming');
const reply = await fetchAssistantReply(data.content);
setMessages((prev) => [...prev, reply]);
setStatus('ready');
}}
/>;export type TMessageRole = 'user' | 'assistant' | 'system';
export type TUserMessage = {
role: 'user';
content: string;
timestamp?: string;
format?: 'plain' | 'markdown';
images?: string[];
fileAttachments?: FileAttachment[];
};
export type TAssistantMessage<TCustomContent = never> = {
role: 'assistant';
content: string | TMessageContent | TMessageContent[];
timestamp?: string;
userRating?: 'like' | 'dislike';
};
export type TChatMessage<TCustomContent = never> = TUserMessage | TAssistantMessage<TCustomContent>;Assistant content can be a plain string, a single typed part ('text' | 'thinking' | 'tool'), or an array of parts. Custom part types extend via the TCustomContent generic.
export type ChatType = {
id: string;
name: string;
createTime: string | null;
lastMessage?: string;
metadata?: Record<string, unknown>;
};
export type ChatStatus = 'submitted' | 'streaming' | 'streaming_loading' | 'ready' | 'error';Full type catalog: src/types/{messages,chat,tool,common}.ts.
Register custom content types via MessageRendererRegistry:
import {
createMessageRendererRegistry,
registerMessageRenderer,
type MessageRendererRegistry,
} from '@gravity-ui/aikit';
type ChartMessageContent = {
type: 'chart';
data: {points: number[]};
};
const renderers: MessageRendererRegistry = createMessageRendererRegistry();
registerMessageRenderer<ChartMessageContent>(renderers, 'chart', {
render: ({content}) => <Chart points={content.data.points} />,
});
<MessageList messages={messages} messageRendererRegistry={renderers} />;See src/components/organisms/MessageList/README.md for the full registry API and validators.
src/adapters/ houses pre-built integrations (e.g. openai). They are imported through the main package entry: import {…} from '@gravity-ui/aikit'.
All styles are controlled through CSS variables in the --g-aikit-* namespace, with fallbacks to Gravity UI's --g-color-* system:
.g-root {
--g-aikit-color-bg-primary: var(--g-aikit-bg-primary, var(--g-color-base-float));
}
[data-theme='dark'] {
--g-aikit-bg-message-user: #0066cc;
}Wrap the application in Gravity UI's <ThemeProvider> and import the matching theme CSS:
import {ThemeProvider} from '@gravity-ui/uikit';
import '@gravity-ui/aikit/themes/common';
import '@gravity-ui/aikit/themes/dark';
<ThemeProvider theme="dark">
<ChatContainer …/>
</ThemeProvider>;Full CSS variable reference: THEMING.md.
React.memoon heavy renderersreact-windowvirtualization for long message lists- Lazy hooks (
useSmartScroll,useScrollPreservation) to avoid layout thrash - Markdown transform cache (
useMarkdownTransform)
All interactive elements expose:
aria-labelfor screen readersrolefor semanticsaria-livefor streaming/loading regions
Enter— send messageShift+Enter— newlineEscape— cancel / closeTab— navigation
Component tests run under Playwright Component Testing — see TESTING.md. For developer guidelines specific to writing components, stories, and tests inside this repo, see guidelines/.