The widget customization layer lives entirely insideDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/jAtInn71/chatwoot-costom/llms.txt
Use this file to discover all available pages before exploring further.
custom/widget/. Each file there is a Vue component or i18n resource that overrides its upstream Chatwoot equivalent via a Dockerfile COPY directive, so upstream source files are never modified. Any change to widget components requires a Vite rebuild (./build.sh) followed by a Docker image rebuild before it takes effect.
Branding override
Upstream Chatwoot renders a “Powered by Chatwoot” footer at the bottom of the widget, wrapped in an anchor tag pointing tohttps://www.chatwoot.com. Even if the i18n key POWERED_BY is relabelled, the outbound link remains in the original component.
This fork replaces the entire component with custom/widget/components/Branding.vue, which renders a plain <span> — no anchor, no Chatwoot logo, no outbound navigation. The Dockerfile maps this file onto the upstream component path at build time.
The display text is controlled by the POWERED_BY key in custom/widget/i18n/en.json:
Chat input enhancements
custom/widget/components/ChatInputWrap.vue wraps the text input and its action buttons. The microphone button (ElevenLabsVoiceButton) is mounted here, next to the emoji picker button:
ElevenLabsVoiceButton itself gates visibility on three conditions being true simultaneously:
isVoiceAgentEnabled— the voice agent feature flag is on for this inbox.provider === 'elevenlabs'— the inbox is configured with the ElevenLabs provider.agentId— a valid agent ID was resolved from the inbox config.
The voice button has a single mount point in
ChatInputWrap.vue. An earlier version also mounted it in HeaderActions.vue; that instance was removed to ensure only one call button appears per widget.Pre-chat form
custom/widget/views/PreChatForm.vue controls the flow before a conversation is created. It collects visitor name, email, phone number, and an initial message, then dispatches them to the server in a specific order:
- The contact record is updated with name and email before the conversation is created, so the AI greeting uses the name just entered rather than a cached name from a previous session.
conversation/createConversationis dispatched with the full set of form fields.- On the
ON_CONVERSATION_CREATEDevent the router navigates to the messages view.
custom/widget/i18n/en.json under the PRE_CHAT_FORM.FIELDS key:
i18n labels
All visible widget text is sourced fromcustom/widget/i18n/en.json. The keys below are the ones most likely to need customisation:
| Key | Default value | Purpose |
|---|---|---|
POWERED_BY | "Powered by Visual Graphx" | Footer branding text |
BUBBLE.LABEL | "Chat with us" | Screen-reader label on the chat bubble |
CHAT_PLACEHOLDER | "chat with us" | Placeholder text in the message input |
START_CONVERSATION | "Start Conversation" | Button on the home view |
TEAM_AVAILABILITY.ONLINE | "We are online" | Status shown when agents are online |
VOICE_AGENT.START_CALL | "Talk to AI" | Tooltip / aria-label on the mic button |
VOICE_AGENT.END_CALL | "End Call" | Label shown during an active voice call |
VOICE_AGENT.CONNECTING | "Connecting..." | Transitional state label |
VOICE_AGENT.MICROPHONE_ACCESS | "Microphone access required. Please allow microphone permission." | Error when mic permission is denied |
custom/widget/i18n/en.json, then run ./build.sh and rebuild the Docker image. Labels are inlined at build time by Vite and are not configurable at runtime.
Header actions
custom/widget/components/HeaderActions.vue renders the action buttons in the widget header. It provides:
- Exit chat button — an icon button (door-with-arrow SVG) that appears when the conversation status is
open,snoozed, orpending. Clicking it opens a confirmation popover with “Cancel” and “Exit Chat” options. - Popout button — opens the conversation in a new browser window. Only rendered when
showPopoutButtonistrue.
endChat method) runs three steps in sequence:
- Calls
toggleStatusto resolve the conversation server-side. - Dispatches
contacts/softExitChatto clear Vuex state and session storage without postingexitChator reloading the iframe. - Posts
{ event: 'closeWindow' }to the parent SDK so the iframe panel collapses.
HeaderActions.vue directly and rebuild.