Skip to content

Appearance & Custom Chatbot Avatar

Merchants configure the storefront chat widget on Appearance (/app/appearance). This doc covers preset avatars, custom image upload (optimized for the storefront), and how settings reach the theme extension.

Related: Admin settings & chat widget · Recent changes · Storefront product cards


RouteFilePurpose
/app/appearanceapp/routes/app.appearance.jsxWidget title, welcome message, theme color, launcher style, bubble position, visibility, launcher animation, icon inset, reply delay
/app/appearance/avatarapp/routes/app.appearance.avatar.jsxUpload or remove custom avatar (multipart POST)

Settings are saved with the Polaris save bar. Custom avatar upload is a separate action (does not require clicking Save on the main form).


PresetBehavior
noneNo avatar in the chat header
15Built-in PNGs at /chat-avatars/{1-5}.png
customMerchant-uploaded image (see below)

Normalization: app/lib/widget-appearance.jsnormalizeChatbotAvatarPreset().


Merchants pick the floating chat button under Theme color on Appearance.

Style IDStorefront
text (default)Oval pill button with widget title text and theme color background
115Preset PNG from /chat-buttons/{id}.png

Normalization: normalizeWidgetLauncherStyle(). Image URL: buildChatButtonImageUrl(style, baseUrl).

Admin thumbnail order is controlled by LAUNCHER_BUTTON_DISPLAY_ORDER in app/lib/widget-appearance.js. Rearrange IDs in that array to change picker order only; saved shop values and storefront URLs still use the image ID (e.g. "5"chat-buttons/5.png).

GET /api/widget-settings returns:

  • widgetLauncherStyletext or 115
  • launcherButtonUrl — absolute URL when an image style is selected; null for text

chat-widget.js renders:

  • text: #appifire-ai-chat-bubble--text with TITLE and theme color
  • image: #appifire-ai-chat-bubble--image with <img src="launcherButtonUrl">

Launcher animation (widgetBubbleAnimation) applies to both modes.


  1. Open Appearance.
  2. Click the Upload circle (next to preset avatars).
  3. Choose JPEG, PNG, WebP, or GIF (max 2 MB).
  4. Server optimizes and selects custom automatically.
  5. Click Save on the appearance form to persist other settings (preset custom is stored on upload).

Remove custom image clears the file and sets preset to none.

Server processing (app/lib/chat-avatar.server.js)

Section titled “Server processing (app/lib/chat-avatar.server.js)”
  • Sharp: auto-rotate (EXIF), crop to square, resize to 512×512, encode WebP (quality ~82).
  • Typical output: ~15–40 KB per shop (header displays at 36px; larger asset avoids blur when scaled down).
  • Saved to: public/chat-avatars/custom/{shopId}.webp (gitignored; .gitkeep only in repo).
  • DB: shops.chatbot_avatar_preset = 'custom', shops.chatbot_avatar_updated_at for cache-busting.

Built by buildChatbotAvatarUrl('custom', baseUrl, { shopId, avatarVersion }):

https://{SHOPIFY_APP_URL}/chat-avatars/custom/{shopId}.webp?v={timestamp}

Returned to the storefront as avatarUrl from GET /api/widget-settings.

Serving in production: uploads are stored under public/chat-avatars/custom/ (not in build/client). The app route chat-avatars.custom.$filename.jsx reads those files from disk. Preset PNGs still come from the static build/client/chat-avatars/ bundle.

Persistence: mount a volume on /app/public/chat-avatars/custom in Docker (or equivalent) so uploads survive redeploys.

extensions/appifire-chat/assets/chat-widget.js:

  • Prefers settings.avatarUrl from the API (works for custom WebP and cache-bust query).
  • Header image: width="36" height="36" (CSS); source file is 512×512 WebP for crisp downscaling. loading="lazy" decoding="async".

ColumnTypeNotes
chatbot_avatar_presetstringnone, custom, or 15
chatbot_avatar_updated_attimestampSet on each successful custom upload
widget_launcher_stylestringtext (default) or 115

Migration: 20260523140000_add_chatbot_avatar_updated_at, 20260531120000_add_widget_launcher_style.


Terminal window
npm install # includes sharp
npx prisma migrate deploy
shopify app deploy # if theme extension JS changed

Uploaded files live on the app server filesystem under public/chat-avatars/custom/. Back up or persist that directory in production if you rely on custom avatars across deploys (or plan object storage later).


FileRole
app/lib/chat-avatar.server.jsValidate, resize, save, delete custom avatar
app/lib/widget-appearance.jsPreset custom, buildChatbotAvatarUrl, launcher style normalization and display order
app/routes/app.appearance.avatar.jsxAdmin upload/remove action
app/routes/app.appearance.jsxUI + loader custom preview URL + launcher style picker
app/routes/api.widget-settings.jsxExposes avatarUrl, widgetLauncherStyle, launcherButtonUrl to storefront
app/routes/chat-avatars.custom.$filename.jsxServes {shopId}.webp from disk in production
public/chat-buttons/Preset launcher button PNGs (1.png15.png)
extensions/appifire-chat/assets/chat-widget.jsRenders header avatar and launcher button from API settings