This document describes the architecture of the AnythingLLM frontend application, including its component structure, communication patterns, and core design principles. The frontend is a single-page React application built with Vite that provides the user interface for workspace management, chat interactions, and system configuration.
For specific implementation details, see:
The frontend is built using the following core technologies:
| Technology | Purpose | Key Files |
|---|---|---|
| React 18 | UI framework | All .jsx files |
| React Router | Client-side routing | frontend/src/App.jsx |
| Vite | Build tool and dev server | frontend/vite.config.js |
| Tailwind CSS | Utility-first styling | frontend/tailwind.config.js |
| i18next | Internationalization | frontend/src/i18n.js |
| @microsoft/fetch-event-source | SSE streaming | frontend/src/models/workspace.js138-203 |
Sources: frontend/package.json frontend/vite.config.js project documentation
Diagram: Component Hierarchy
The application follows a hierarchical component structure where App.jsx serves as the root, managing routing and global providers. The Sidebar and main content area are rendered side-by-side, with the sidebar handling workspace/thread navigation and the main area displaying chat interfaces or settings pages.
Sources: frontend/src/App.jsx frontend/src/components/Sidebar/index.jsx19-81 frontend/src/components/WorkspaceChat/ChatContainer/index.jsx27-325
ChatContainer)The ChatContainer component orchestrates all chat-related functionality:
Diagram: ChatContainer Data Flow
The ChatContainer manages three distinct flows:
multiplexStream → SSE streaming → handleChat updates historyhandleSocketResponse updates historySources: frontend/src/components/WorkspaceChat/ChatContainer/index.jsx27-325 frontend/src/utils/chat/index.js frontend/src/utils/chat/agent.js
PromptInput)The PromptInput component handles message composition with advanced features:
| Feature | Implementation | Key Code |
|---|---|---|
| Auto-resize textarea | adjustTextArea function | frontend/src/components/WorkspaceChat/ChatContainer/PromptInput/index.jsx185-189 |
| Undo/redo stack | undoStack and redoStack refs | frontend/src/components/WorkspaceChat/ChatContainer/PromptInput/index.jsx47-48 |
| Draft persistence | usePromptInputStorage hook | frontend/src/hooks/usePromptInputStorage.js27-63 |
| Slash commands | useSlashCommands hook | frontend/src/components/WorkspaceChat/ChatContainer/PromptInput/index.jsx43 |
| Agent invocation | useAvailableAgents hook | frontend/src/components/WorkspaceChat/ChatContainer/PromptInput/index.jsx42 |
| Paste handling | Custom handlePasteEvent | frontend/src/components/WorkspaceChat/ChatContainer/PromptInput/index.jsx191-239 |
The component uses a custom event system to prevent unnecessary re-renders. Instead of passing promptInput as a prop, it emits PROMPT_INPUT_EVENT to update the textarea value remotely:
Sources: frontend/src/components/WorkspaceChat/ChatContainer/PromptInput/index.jsx33-347 frontend/src/hooks/usePromptInputStorage.js
ChatHistory)The ChatHistory component renders messages with scroll management:
Key Responsibilities:
HistoricalMessage, PromptReply, Chartable)EditMessageFormDiagram: ChatHistory Features
The scroll management system distinguishes between user-initiated scrolling and auto-scrolling. When streaming (isStreaming === true), the component auto-scrolls to the bottom unless the user has manually scrolled up (isUserScrolling === true).
Sources: frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/index.jsx28-381 frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/HistoricalMessage/index.jsx24-270
Sidebar)The sidebar provides workspace and thread navigation:
Diagram: Sidebar Structure
The sidebar uses react-beautiful-dnd for drag-and-drop workspace reordering. Workspace order is persisted to localStorage via Workspace.storeWorkspaceOrder(). When a workspace is active, its ThreadContainer is rendered below it, showing all threads with similar drag-and-drop functionality.
Sources: frontend/src/components/Sidebar/index.jsx19-229 frontend/src/components/Sidebar/ActiveWorkspaces/index.jsx17-213 frontend/src/components/Sidebar/ActiveWorkspaces/ThreadContainer/index.jsx10-234
The frontend uses model objects as API client abstractions, not to be confused with backend models. These are located in frontend/src/models/ and provide clean interfaces for API communication:
Diagram: Frontend Model to Backend API Mapping
Each model exports a single object with methods that correspond to backend API endpoints. Methods use fetch with baseHeaders() for authentication:
Sources: frontend/src/models/workspace.js8-579 frontend/src/utils/request.js
Chat responses stream from the backend using SSE via @microsoft/fetch-event-source:
Diagram: SSE Streaming Flow
The SSE connection remains open during response generation. Each event contains a JSON payload that handleChat processes to update the UI. The backend can send various event types:
| Event Type | Purpose | Handler Action |
|---|---|---|
textResponseChunk | Streaming text chunk | Append to response content |
finalizeResponseStream | Response complete | Mark streaming finished |
abort | Error occurred | Display error message |
stopGeneration | User stopped generation | Reset UI state |
Sources: frontend/src/models/workspace.js138-203 frontend/src/utils/chat/index.js frontend/src/components/WorkspaceChat/ChatContainer/index.jsx177-222
Agent interactions use WebSocket for bidirectional communication:
The WebSocket connection is established when socketId is set by the backend during an agent invocation. The connection remains open for the duration of the agent session, allowing for multi-turn interactions.
Sources: frontend/src/components/WorkspaceChat/ChatContainer/index.jsx225-295 frontend/src/utils/chat/agent.js
The frontend uses custom events for decoupled component communication:
| Event Name | Purpose | Emitter | Listener |
|---|---|---|---|
PROMPT_INPUT_EVENT | Update prompt text remotely | ChatContainer, Commands | PromptInput |
ABORT_STREAM_EVENT | Stop chat generation | StopGenerationButton | ChatContainer, Workspace model |
CLEAR_ATTACHMENTS_EVENT | Clear file attachments | ChatContainer | DnDWrapper |
ATTACHMENTS_PROCESSING_EVENT | Disable send during upload | DnDWrapper | PromptInput |
ATTACHMENTS_PROCESSED_EVENT | Enable send after upload | DnDWrapper | PromptInput |
AGENT_SESSION_START | Agent session begins | ChatContainer | UI components |
AGENT_SESSION_END | Agent session ends | ChatContainer | UI components |
THREAD_RENAME_EVENT | Thread name changed | ThreadItem | ThreadContainer |
Example: Prompt Input Remote Update
This pattern prevents prop drilling and reduces re-renders by avoiding direct parent-child state dependencies.
Sources: frontend/src/components/WorkspaceChat/ChatContainer/PromptInput/index.jsx29-74 frontend/src/components/WorkspaceChat/ChatContainer/DnDWrapper.jsx frontend/src/components/Sidebar/ActiveWorkspaces/ThreadContainer/index.jsx8
The frontend uses a hybrid state management approach:
Most components use React hooks for local state:
When to use: Component-specific UI state, transient data, loading flags.
Limited use of Context for truly global state:
| Context | Purpose | File |
|---|---|---|
DndUploaderContext | File attachment state | frontend/src/components/WorkspaceChat/ChatContainer/DnDWrapper.jsx |
MetricsProvider | Performance metrics tracking | frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/HistoricalMessage/Actions/RenderMetrics.jsx |
ThoughtExpansionProvider | LLM reasoning display state | frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/ThoughtContainer.jsx |
Persistent state is stored in localStorage:
| Key | Purpose | Format |
|---|---|---|
anythingllm-workspace-order | Workspace display order | number[] (workspace IDs) |
anythingllm_sidebar_toggle | Sidebar visibility state | "open" or "closed" |
USER_PROMPT_INPUT_MAP | Unsent message drafts | {[slug]: string} |
LAST_VISITED_WORKSPACE | Last viewed workspace | {slug: string, name: string} |
Example: Draft Persistence
The usePromptInputStorage hook automatically syncs prompt input to localStorage:
Sources: frontend/src/hooks/usePromptInputStorage.js27-63 frontend/src/models/workspace.js526-555 frontend/src/components/Sidebar/SidebarToggle/index.jsx5-68
frontend/src/
├── components/ # React components
│ ├── Sidebar/ # Workspace navigation
│ │ ├── index.jsx
│ │ ├── ActiveWorkspaces/
│ │ │ ├── index.jsx
│ │ │ └── ThreadContainer/
│ │ └── SearchBox/
│ ├── WorkspaceChat/ # Chat interface
│ │ └── ChatContainer/
│ │ ├── index.jsx
│ │ ├── ChatHistory/
│ │ │ ├── index.jsx
│ │ │ ├── HistoricalMessage/
│ │ │ └── PromptReply/
│ │ ├── PromptInput/
│ │ └── DnDWrapper.jsx
│ ├── DefaultChat/ # Home screen
│ ├── Modals/ # Modal dialogs
│ ├── Footer/ # Footer links
│ └── SettingsButton/ # Settings navigation
├── models/ # API client abstractions
│ ├── workspace.js
│ ├── system.js
│ ├── user.js
│ └── appearance.js
├── hooks/ # Custom React hooks
│ ├── useUser.js
│ ├── usePromptInputStorage.js
│ └── useChatHistoryScrollHandle.js
├── utils/ # Utility functions
│ ├── chat/ # Chat-related utilities
│ │ ├── index.js # handleChat function
│ │ ├── agent.js # Agent WebSocket handling
│ │ └── markdown.js # Markdown rendering
│ ├── request.js # HTTP utilities
│ └── paths.js # Route definitions
├── pages/ # Route pages
├── i18n.js # i18n configuration
└── App.jsx # Application root
Key Conventions:
index.jsx as entry pointuse, located in frontend/src/hooks/Sources: frontend/src/ directory structure
The application detects mobile devices using react-device-detect:
On mobile, the sidebar transforms into a slide-out drawer accessible via hamburger menu:
Desktop Sidebar:
Ctrl/Cmd + Shift + SMobile Sidebar:
SidebarMobileHeaderSources: frontend/src/components/Sidebar/index.jsx83-189 frontend/src/components/WorkspaceChat/ChatContainer/index.jsx297-325
Components use React.memo to prevent unnecessary re-renders:
Sources: frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/HistoricalMessage/index.jsx184-197 frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/PromptReply/index.jsx149
User input is debounced to reduce API calls and state updates:
Sources: frontend/src/components/Sidebar/SearchBox/index.jsx23 frontend/src/components/WorkspaceChat/ChatContainer/PromptInput/index.jsx94 frontend/src/hooks/usePromptInputStorage.js41-54
Routes and components are not explicitly lazy-loaded in the current implementation, but the build process (Vite) automatically code-splits by route.
Many components follow this pattern:
ChatContainer, ThreadContainer - Handles logic, state, API callsChatHistory, ThreadItem - Receives props, renders UISome components are composed of multiple sub-components:
Logic is extracted into custom hooks:
useUser(): Current user stateusePromptInputStorage(): Draft persistenceuseChatHistoryScrollHandle(): Scroll behavioruseSlashCommands(): Command menu visibilityuseSidebarToggle(): Sidebar state and keyboard shortcutsSources: frontend/src/hooks/ directory, component implementations
Model methods use try-catch with fallback values:
Error messages are displayed inline rather than using global error boundaries:
Sources: frontend/src/models/workspace.js frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/HistoricalMessage/index.jsx56-78
<nav>, <main>, <button>, etc.aria-label, aria-current, role="list"sr-only classExample: Accessible Workspace List
Sources: frontend/src/components/Sidebar/ActiveWorkspaces/index.jsx79-100 frontend/src/components/WorkspaceChat/ChatContainer/PromptInput/index.jsx306-307
Refresh this wiki