Chat Avatar Rendering#
Dify's chat UI uses four distinct avatar components, each serving a different surface. Understanding which component to use β and how the icon-type resolution works β is the key to working in this area.
Component Map#
| Component | Path | Renders |
|---|---|---|
AnswerIcon | base/answer-icon/index.tsx | Bot reply avatar in chat thread |
AppIcon | base/app-icon/index.tsx | App icon across the product (lists, headers, etc.) |
WorkspaceAvatar | base/workspace-avatar/index.tsx | Workspace identity in nav & member settings |
LogoEmbeddedChatAvatar | base/logo/logo-embedded-chat-avatar.tsx | Static logo in the embedded chatbot widget |
Rendering Priority & Fallback Logic#
Both AnswerIcon and AppIcon follow the same three-tier priority :
- Image URL β when
iconType === 'image'andimageUrlis present, renders an<img>tag filling the container. - Emoji β when
iconis a non-empty string, renders<em-emoji id={icon} />via emoji-mart. - Robot fallback β renders
<em-emoji id="π€" />when neither condition is met.
The AppIconType type (from @/types/app) discriminates between 'image' and emoji icon modes.
Key Components#
AnswerIcon#
AnswerIcon is a lightweight component specifically for the bot-side avatar in a chat message. It always renders as a full-width/height circle (rounded-full). The background defaults to #D5F5F6 if no background prop is provided . Props: iconType, icon, background, imageUrl.
emoji-mart is initialized once at module load via init({ data }) . The <em-emoji> element is a Web Component registered by emoji-mart.
AppIcon#
AppIcon is the general-purpose icon for apps and datasets. It adds:
- Size variants (
xsβxxl) viacva. - Rounded prop to toggle
rounded-fullvs. the size's default border radius. - Hydration guard β on the server, emojis are rendered as raw Unicode; on the client, they switch to
<em-emoji>viauseSyncExternalStore. - Edit overlay β
showEditIconprop activates a hover overlay withRiEditLine. - Default background:
#FFEAD5.
WorkspaceAvatar#
WorkspaceAvatar renders a square initial-based avatar (no image or emoji support). It displays the first character of the workspace name in uppercase, or '?' as fallback . Built on AvatarRoot/AvatarFallback from @langgenius/dify-ui. Supported sizes: xs | sm | lg | 2xl . This component was introduced to unify previously inconsistent workspace avatar rendering across navigation and Members settings .
LogoEmbeddedChatAvatar#
LogoEmbeddedChatAvatar is a static <img> pointing to ${basePath}/logo/logo-embedded-chat-avatar.png β a fixed asset, not app-configurable. Used at the top of the embedded chatbot widget.
Chat Thread Integration#
In Answer, the avatar container is a size-10 div. It renders the answerIcon prop if provided; otherwise falls back to <AnswerIcon /> with no props (pure robot emoji). Callers (e.g., the full chat page or embedded chatbot) are responsible for constructing an <AnswerIcon iconType={...} icon={...} imageUrl={...} /> element and passing it as answerIcon.
The top-level Chat component exposes both questionIcon and answerIcon as ReactNode props, letting callers supply any icon shape for either side of the conversation.
emoji-mart & SSR#
emoji-mart is excluded from SSR pre-bundling via ssr.noExternal: ['emoji-mart'] in the Vite config to avoid CJS named-export issues. The AppIcon hydration guard handles the SSR mismatch: server renders raw Unicode, client upgrades to <em-emoji> Web Components after hydration .