Better Helpdesk
Widget

Widget reference

Props, attributes, DOM events and CSS custom properties of HelpdeskWidget, HelpdeskConversations, their custom elements and the script tag.

The widget is the customer side of the helpdesk. It comes in two forms over the same widget/* routes:

FormReactCustom elementRegisters it
A launcher and a floating panelHelpdeskWidget<helpdesk-widget>defineHelpdeskWidget()
The panel in the page, always open, without a launcherHelpdeskConversations<helpdesk-conversations>defineHelpdeskConversations()

All four exports come from better-helpdesk/widget, which starts with 'use client'. Each element renders into its own open shadow root, so the host's styles do not reach it. Related guides: Add the widget, Add a support page, Theme the widget and the agent UI.

React props

HelpdeskWidget takes every prop below. HelpdeskConversations takes all but label.

Prop

Type

app/layout.tsx
import { HelpdeskWidget } from 'better-helpdesk/widget';

<HelpdeskWidget inbox="support" locale="en" context={{ plan: 'team' }} />

Element attributes

<helpdesk-widget> and <helpdesk-conversations> read the same values as attributes. A changed attribute takes effect at once.

AttributePropValue
apiapiURL
inboxinboxInbox key
localelocaleen, de, fr, it, or a browser language tag
typestypesComma-separated list
orgorgIdOrganization id
identity-tokenidentityTokenJWT
app-versionappVersionString
labellabelString, <helpdesk-widget> only
themenoneauto follows the operating system's dark mode. The React wrappers take it as the theme prop.

The context property, not an attribute, carries the host context: element.context = { plan: 'team' }.

import { defineHelpdeskWidget } from 'better-helpdesk/widget';

defineHelpdeskWidget();

defineHelpdeskWidget(tag = 'helpdesk-widget') and defineHelpdeskConversations(tag = 'helpdesk-conversations') register the elements under another tag name when given one. Each does nothing on the server or when the tag is already registered.

Script tag

widget.js in the published package (better-helpdesk/widget.js, the file dist/widget.js) is a standalone build with React included. The handler does not serve it: the host serves the file itself, in the example below from the root of its site (Add the widget copies it into public/). The script registers <helpdesk-widget>, then appends one to document.body with an attribute for each data-* attribute of the script tag: data-api becomes api, data-identity-token becomes identity-token, data-theme becomes theme.

<script
  src="https://app.example.com/widget.js"
  data-api="https://app.example.com/api/helpdesk"
  data-inbox="sales"
  data-locale="de"
  async
></script>

Only the element the script creates comes alive. A <helpdesk-widget> already in the page's markup stays inert. Loading the script again while its widget is still in the page adds no second one.

Element methods

MethodWhat it does
open({ type?, subject?, message? })Opens the panel. With type, such as 'bug', it opens on that type's form when the inbox offers it, and on its first view otherwise; focus moves into the form. subject and message go into the form's fields the customer has left empty, and the customer can change them before sending; the subject field shows on bug, feature and question forms. On a panel that is already open, it keeps a draft the customer is writing unless type names another form, and it never leaves a conversation the customer is replying in.

<helpdesk-widget> and <helpdesk-conversations> both have it. Code that holds the element calls it directly, for example from a button of the host's own:

document.querySelector('helpdesk-widget')?.open({
  type: 'bug',
  message: 'The export button did nothing.',
});

With the React wrapper, pass a ref:

import { HelpdeskWidget, type HelpdeskWidgetElement } from 'better-helpdesk/widget';
import { useRef } from 'react';

const widget = useRef<HelpdeskWidgetElement>(null);
// <HelpdeskWidget ref={widget} />
// later: widget.current?.open({ type: 'bug' });

HelpdeskWidgetElement is the element's type and OpenRequest the options of open(); the package also registers both tags with TypeScript, so document.querySelector('helpdesk-widget') is typed without a cast.

DOM events

The element dispatches these on itself. They bubble and cross the shadow boundary.

EventdetailWhen
helpdesk:opennoneThe panel opens. <helpdesk-widget> only.
helpdesk:message-sent{ inbox: string, type: string, segment?: string, reference: string }A new conversation is sent.
helpdesk:booking-clicked{ reference: string }The customer follows the booking link.
document.addEventListener('helpdesk:message-sent', event => {
  const { reference } = (event as CustomEvent<{ reference: string }>).detail;
});

Key presses in the widget's text fields stop at the element, so they do not reach the host page's shortcuts.

What the widget captures

A message sent with Send us a message is a chat and carries the page URL and locale. A ticket for a bug, feature or question is a ticket, and its context holds the page URL and title, user agent, viewport, locale, appVersion, the last 10 errors and unhandled rejections on the page (300 characters each), and the host context as host. The form lists each item, and the customer can leave any of them out. On an inbox with priorities, a ticket form also asks a signed-in customer of a tier the inbox offers them to to pick one, named by priorityLabels and explained by the inbox's text; a visitor is never asked. Once sent, the thread says by when the team aims to reply when the conversation falls under a response target, and after resolution asks for a rating: good or bad, or 1 to 5 with ratingScale: 5. For any other type, it holds the page URL, the locale, the first page of the visit, the external referrer and the UTM parameters of that first page. The fields and their limits are on HTTP routes.

Browser storage

KeyStorageHolds
helpdesk-visitor:{api}localStorageAn anonymous visitor's token, per API base URL without its trailing slash.
helpdesk-visitorlocalStorageRead when the key above is empty; written by earlier versions.
helpdesk-landingsessionStorageThe first page of the visit.
helpdesk-referrersessionStorageThe external referrer of the visit, or an empty string.
helpdesk-searches:{api}localStorageThe last five documentation searches, shown on Home.

CSS custom properties

Set on the element or any ancestor; the values inherit into the shadow root.

PropertyDefaultStyles
--helpdesk-fontsystem-ui, -apple-system, 'Segoe UI', sans-serifFont family
--helpdesk-bg#ffffffPanel background
--helpdesk-fg#0c2034Text; shadows are tinted from it
--helpdesk-muted#486581Secondary text
--helpdesk-border#d9e2ecBorders
--helpdesk-subtle#f0f4f8Quiet backgrounds
--helpdesk-accent#0c2034Buttons, links, unread dots
--helpdesk-accent-hover#243b53Accent on hover
--helpdesk-accent-fg#f0f4f8Text on the accent
--helpdesk-danger#cf1124Errors
--helpdesk-focus#f55068Focus ring
--helpdesk-radius12pxCorners; controls step down from it and dialogs step up
--helpdesk-launcher-bgthe accentLauncher background
--helpdesk-launcher-fgthe accent foregroundLauncher icon and label
--helpdesk-panel-header-bgthe accentPanel header background
--helpdesk-panel-header-fgthe accent foregroundPanel header text
--helpdesk-panel-border1px solid the border colorPanel border
--helpdesk-shadowa soft shadow tinted from the text colorShadows of the launcher, the panel and pop-ups; none removes them
--helpdesk-offset-bottom20pxDistance of the widget from the bottom of the viewport
--helpdesk-page-height640pxHeight of <helpdesk-conversations>

With theme="auto" and a dark operating system theme, unset properties take dark defaults: background #102a43, text #f0f4f8, muted #bcccdc, border #334e68, subtle #243b53, accent #f0f4f8, accent hover #d9e2ec, accent foreground #0c2034, panel header #243b53 on #f0f4f8.

The agent UI reads most of the same properties; see Agent UI.

On this page