Chapter 7: Advanced Customization
March 21, 2026 Β· View on GitHub
Welcome to Chapter 7: Advanced Customization. In this part of LobeChat AI Platform: Deep Dive Tutorial, you will build an intuitive mental model first, then move into concrete implementation details and practical production tradeoffs.
Deep dive into LobeChat's theme engine, i18n, monorepo architecture, and component system
π― Learning Objectives
By the end of this chapter, you'll understand:
- The theme engine and custom theming
- Internationalization (i18n) system
- Monorepo architecture and package structure
- Custom component development
- Extending the agent/assistant system
Theme Engine
LobeChat uses Ant Design tokens combined with a custom theme system for comprehensive visual customization.
Theme Provider Architecture
// Theme token structure
interface LobeThemeTokens {
// Color palette
colorPrimary: string;
colorBgLayout: string;
colorBgContainer: string;
colorText: string;
colorTextSecondary: string;
// Chat-specific tokens
chatBubbleBg: string;
chatBubbleUserBg: string;
chatAvatarSize: number;
chatMessageMaxWidth: string;
// Layout tokens
sidebarWidth: number;
headerHeight: number;
borderRadius: number;
// Typography
fontFamily: string;
fontSize: number;
fontSizeHeading: number;
}
// Theme provider wraps the app
const ThemeProvider: React.FC<{ children: React.ReactNode }> = ({
children,
}) => {
const { theme, isDark } = useThemeStore();
const tokens = useMemo(() => ({
...defaultTokens,
...theme.customTokens,
colorPrimary: theme.primaryColor,
algorithm: isDark ? darkAlgorithm : defaultAlgorithm,
}), [theme, isDark]);
return (
<AntdConfigProvider theme={{ token: tokens }}>
<StyledThemeProvider theme={tokens}>
{children}
</StyledThemeProvider>
</AntdConfigProvider>
);
};
Creating Custom Themes
// Define a custom theme
const oceanTheme: CustomTheme = {
name: "Ocean",
primaryColor: "#0077B6",
appearance: "dark",
customTokens: {
colorBgLayout: "#0A1628",
colorBgContainer: "#0D2137",
chatBubbleBg: "#163854",
chatBubbleUserBg: "#0077B6",
borderRadius: 16,
fontFamily: "'Inter', system-ui, sans-serif",
},
};
// Register theme in settings
const themeStore = create<ThemeStore>((set) => ({
themes: [defaultTheme, darkTheme, oceanTheme],
activeTheme: "default",
setTheme: (themeName: string) => {
set({ activeTheme: themeName });
// Persist to localStorage
localStorage.setItem("lobe-theme", themeName);
},
addCustomTheme: (theme: CustomTheme) => {
set((state) => ({
themes: [...state.themes, theme],
}));
},
}));
CSS Variable System
LobeChat exposes theme values as CSS variables:
:root {
--lobe-primary: #0077b6;
--lobe-bg-layout: #0a1628;
--lobe-bg-container: #0d2137;
--lobe-text: #e2e8f0;
--lobe-text-secondary: #94a3b8;
--lobe-chat-bubble: #163854;
--lobe-chat-user-bubble: #0077b6;
--lobe-border-radius: 16px;
--lobe-sidebar-width: 280px;
--lobe-header-height: 64px;
}
/* Usage in custom components */
.custom-card {
background: var(--lobe-bg-container);
color: var(--lobe-text);
border-radius: var(--lobe-border-radius);
}
Internationalization (i18n)
LobeChat supports 20+ languages through a structured i18n system.
Translation Architecture
src/
βββ locales/
β βββ default/
β β βββ chat.ts # Chat-related strings
β β βββ common.ts # Shared strings
β β βββ setting.ts # Settings strings
β β βββ plugin.ts # Plugin strings
β β βββ index.ts # Namespace exports
β βββ en-US/ # English (base)
β βββ zh-CN/ # Chinese Simplified
β βββ ja-JP/ # Japanese
β βββ de-DE/ # German
β βββ ... # 20+ languages
Translation Files
// locales/default/chat.ts
export default {
input: {
placeholder: "Type a message...",
send: "Send",
stop: "Stop generating",
regenerate: "Regenerate",
},
message: {
copy: "Copy",
delete: "Delete",
edit: "Edit",
retry: "Retry",
},
sidebar: {
newChat: "New Chat",
history: "Chat History",
settings: "Settings",
plugins: "Plugins",
},
model: {
select: "Select Model",
temperature: "Temperature",
maxTokens: "Max Tokens",
},
} as const;
Using Translations in Components
import { useTranslation } from "react-i18next";
const ChatInput: React.FC = () => {
const { t } = useTranslation("chat");
return (
<div>
<textarea placeholder={t("input.placeholder")} />
<button>{t("input.send")}</button>
</div>
);
};
Adding a New Language
// 1. Create locale directory: locales/fr-FR/
// 2. Create translation files matching the default structure
// locales/fr-FR/chat.ts
export default {
input: {
placeholder: "Tapez un message...",
send: "Envoyer",
stop: "ArrΓͺter la gΓ©nΓ©ration",
regenerate: "RΓ©gΓ©nΓ©rer",
},
// ... complete translations
};
// 3. Register in the locale index
// locales/index.ts
import frFR from "./fr-FR";
export const locales = {
"en-US": enUS,
"fr-FR": frFR,
// ...
};
Monorepo Architecture
LobeChat is organized as a pnpm workspace monorepo:
lobe-chat/
βββ src/ # Main Next.js application
β βββ app/ # App Router pages
β βββ components/ # React components
β βββ services/ # Business logic services
β βββ store/ # Zustand state stores
β βββ utils/ # Utility functions
β βββ locales/ # i18n translations
βββ packages/ # Shared packages
β βββ @lobehub/ui/ # UI component library
β βββ @lobehub/icons/ # Icon set
β βββ @lobehub/tts/ # Text-to-speech
β βββ @lobehub/lint/ # Linting configs
βββ contributing/ # Contributor resources
βββ docs/ # Documentation
State Management (Zustand)
LobeChat uses Zustand with slices pattern:
// store/chat/store.ts
import { create } from "zustand";
import { devtools, persist } from "zustand/middleware";
interface ChatState {
messages: ChatMessage[];
activeSessionId: string | null;
isGenerating: boolean;
}
interface ChatActions {
sendMessage: (content: string) => Promise<void>;
regenerateMessage: (messageId: string) => Promise<void>;
deleteMessage: (messageId: string) => void;
clearMessages: () => void;
setActiveSession: (sessionId: string) => void;
}
type ChatStore = ChatState & ChatActions;
export const useChatStore = create<ChatStore>()(
devtools(
persist(
(set, get) => ({
messages: [],
activeSessionId: null,
isGenerating: false,
sendMessage: async (content) => {
const message: ChatMessage = {
id: crypto.randomUUID(),
role: "user",
content,
timestamp: Date.now(),
};
set((state) => ({
messages: [...state.messages, message],
isGenerating: true,
}));
// Trigger AI response
await get().generateResponse(message);
set({ isGenerating: false });
},
deleteMessage: (messageId) => {
set((state) => ({
messages: state.messages.filter(m => m.id !== messageId),
}));
},
clearMessages: () => set({ messages: [] }),
setActiveSession: (sessionId) => {
set({ activeSessionId: sessionId });
},
}),
{ name: "lobe-chat-store" }
)
)
);
Custom Agent/Assistant Configuration
Agent Schema
interface Agent {
id: string;
name: string;
description: string;
avatar: string;
systemPrompt: string;
// Model configuration
model: {
provider: string;
id: string;
temperature: number;
maxTokens: number;
topP: number;
frequencyPenalty: number;
presencePenalty: number;
};
// Capabilities
plugins: string[]; // Enabled plugin IDs
files: boolean; // File upload support
knowledge: string[]; // Knowledge base IDs
tts: TTSConfig | null; // Text-to-speech config
// Behavior
tags: string[];
category: string;
visibility: "public" | "private";
}
Creating Custom Agents
const codeReviewAgent: Agent = {
id: "code-review-agent",
name: "Code Review Expert",
description: "Expert code reviewer focusing on security and performance",
avatar: "π",
systemPrompt: `You are an expert code reviewer. When reviewing code:
1. Check for security vulnerabilities (OWASP Top 10)
2. Identify performance bottlenecks
3. Suggest improvements with specific code examples
4. Rate each finding as Critical/High/Medium/Low
5. Always explain WHY something is a problem, not just WHAT`,
model: {
provider: "anthropic",
id: "claude-sonnet-4-20250514",
temperature: 0.3,
maxTokens: 8192,
topP: 0.9,
frequencyPenalty: 0,
presencePenalty: 0,
},
plugins: [],
files: true,
knowledge: [],
tts: null,
tags: ["development", "code-review", "security"],
category: "development",
visibility: "private",
};
Custom Component Development
Chat Bubble Customization
// components/ChatBubble/index.tsx
import { memo } from "react";
import { useTheme } from "@lobehub/ui";
interface ChatBubbleProps {
role: "user" | "assistant";
content: string;
timestamp: number;
actions?: React.ReactNode;
}
export const ChatBubble = memo<ChatBubbleProps>(
({ role, content, timestamp, actions }) => {
const theme = useTheme();
const isUser = role === "user";
return (
<div style={{
display: "flex",
flexDirection: isUser ? "row-reverse" : "row",
gap: "12px",
maxWidth: "80%",
alignSelf: isUser ? "flex-end" : "flex-start",
}}>
<div style={{
padding: "12px 16px",
borderRadius: theme.borderRadius,
backgroundColor: isUser
? theme.chatBubbleUserBg
: theme.chatBubbleBg,
color: theme.colorText,
}}>
<div dangerouslySetInnerHTML={{
__html: renderMarkdown(content)
}} />
<div style={{
fontSize: "12px",
color: theme.colorTextSecondary,
marginTop: "4px",
}}>
{new Date(timestamp).toLocaleTimeString()}
</div>
</div>
{actions && (
<div style={{ opacity: 0, transition: "opacity 0.2s" }}>
{actions}
</div>
)}
</div>
);
}
);
Environment Configuration
// Custom environment overrides
// .env.local
NEXT_PUBLIC_BASE_PATH=/chat
NEXT_PUBLIC_CUSTOM_MODELS=my-model-1,my-model-2
NEXT_PUBLIC_DEFAULT_AGENT_CONFIG={"model":"claude-sonnet-4-20250514","temperature":0.7}
NEXT_PUBLIC_ENABLE_KNOWLEDGE_BASE=true
NEXT_PUBLIC_ANALYTICS_ID=UA-XXXXX
Summary
| Concept | Key Takeaway |
|---|---|
| Theme Engine | Ant Design tokens + CSS variables for complete visual customization |
| i18n | 20+ languages via react-i18next with namespace-based translation files |
| Monorepo | pnpm workspaces with shared packages (@lobehub/ui, icons, tts) |
| State Management | Zustand with slices pattern and persistence middleware |
| Agents | JSON-configurable with system prompts, model settings, and plugins |
| Components | React components with theme-aware styling via token system |
Next Steps: Chapter 8: Scaling & Performance β Optimize LobeChat for production with caching, database tuning, and edge deployment.
Built with insights from the LobeChat repository and community documentation.
What Problem Does This Solve?
Most teams struggle here because the hard part is not writing more code, but deciding clear boundaries for theme, lobe, chat so behavior stays predictable as complexity grows.
In practical terms, this chapter helps you avoid three common failures:
- coupling core logic too tightly to one implementation path
- missing the handoff boundaries between setup, execution, and validation
- shipping changes without clear rollback or observability strategy
After working through this chapter, you should be able to reason about Chapter 7: Advanced Customization as an operating subsystem inside LobeChat AI Platform: Deep Dive Tutorial, with explicit contracts for inputs, state transitions, and outputs.
Use the implementation notes around state, locales, messages as your checklist when adapting these patterns to your own repository.
How it Works Under the Hood
Under the hood, Chapter 7: Advanced Customization usually follows a repeatable control path:
- Context bootstrap: initialize runtime config and prerequisites for
theme. - Input normalization: shape incoming data so
lobereceives stable contracts. - Core execution: run the main logic branch and propagate intermediate state through
chat. - Policy and safety checks: enforce limits, auth scopes, and failure boundaries.
- Output composition: return canonical result payloads for downstream consumers.
- Operational telemetry: emit logs/metrics needed for debugging and performance tuning.
When debugging, walk this sequence in order and confirm each stage has explicit success/failure conditions.
Source Walkthrough
Use the following upstream sources to verify implementation details while reading this chapter:
- LobeChat
Why it matters: authoritative reference on
LobeChat(github.com).
Suggested trace strategy:
- search upstream code for
themeandlobeto map concrete implementation paths - compare docs claims against actual runtime/config code before reusing patterns in production