Sizes and budgets#
| Bundle | Loads | Size (brotli) | Budget |
|---|---|---|---|
| Launcher | On every page with the snippet | 7.8 KB | 8 KB |
| Helper | After your page's load event, only when your page passes a user or an unread reply is waiting | 2.3 KB | 3 KB |
| Chat panel | When a visitor opens the chat, once per page view. Never before your page's load event | 59.5 KB | 60 KB |
| Language file | Only for a visitor whose language needs it, one of 8 | up to 2.9 KB | 3 KB |
The chat panel loads only when a visitor opens the chat. A page that passes a user (with identify or bettercomConfig.user), or a visitor with an unread reply, gets the small helper after your load event instead: it sends the user and draws the preview bubble. A page with neither fetches nothing more.
Sizes are brotli at quality 11, from the widget build of . The widget build measures every bundle the same way and fails when one is over its budget, so a release cannot quietly grow past these lines.
What loads, in order#
- Your page parses and renders.
The script tag is
async, so the browser keeps working on your page while it downloads. - The launcher fetches your workspace settings.
Brand color, corner, size and copy usually arrive before anything is drawn. If they take longer than 3 seconds, the launcher goes ahead with defaults.
- It records the page view.
With tracking on: one small request, then one per in-page navigation.
- It waits for your page.
After the window
loadevent plus Show after (1 second by default), or, with Wait for the page to finish drawing (Settings, Widget, Loading) on, after your Largest Contentful Paint settles (at most 8 seconds). - It draws the launcher.
Inside a closed shadow root. While the panel is closed and the tab is visible, it checks for unread messages every 30 seconds and when the tab comes back into view. Hidden tabs do not ask.
- After load, only if needed: the helper.
When your page passed a user, or an unread reply is waiting to be shown in the preview bubble, the helper downloads now, after your
loadevent. It sends the user and draws the bubble. The chat panel does not load. - A visitor opens the chat.
The chat panel downloads on the first open, once per page view, and opens its connection to the chat server.
Compression and caching#
The server keeps a brotli and a gzip copy of each bundle and sends the best one your visitor's browser accepts. In production the loader is cached for an hour (Cache-Control: public, max-age=3600). The helper and the chat panel are the same files for every workspace and are cached for a year under their ?v= version (public, max-age=31536000, immutable); a new build is a new URL.
Content Security Policy#
If your site sends a Content-Security-Policy header, allow the widget's host in these directives. YOUR_HOST is the host in your install snippet.
script-src 'self' https://YOUR_HOST;
connect-src 'self' https://YOUR_HOST wss://YOUR_HOST;
img-src 'self' https://YOUR_HOST blob:;
media-src 'self' https://YOUR_HOST blob:;| Directive | Why the widget needs it |
|---|---|
script-src | The loader, the helper, the chat panel and the language files, all served from your host. |
connect-src | Settings, page views, unread checks and the waveform of a voice message over HTTPS, and the live chat connection over a WebSocket (wss:), which also sets up voice calls. |
img-src | Your logo, avatars and uploaded images, plus blob: for previews of files a visitor is about to send. |
media-src | Voice messages and video files, plus blob: to play a voice message back before it is sent. |
style-src | Nothing to add: the widget's styles are constructed stylesheets inside its shadow root, which style-src does not govern. Safari before 16.4 has no constructed stylesheets and uses a <style> element there instead, which needs style-src 'unsafe-inline'. |
The widget loads no fonts and no third-party scripts, so font-src needs nothing.
Voice calls: the call is set up over the chat connection above. Its audio then travels over WebRTC, through the STUN and TURN servers the chat server names when a call starts. We have not yet tested calls under a strict policy, so this page lists no sources for them.
Check it yourself#
- Open your page with developer tools on the Network tab, cache disabled, and filter by
widget. - Reload. You should see the loader and the settings request. Before the
loadevent there is nothing else. After it,helper.jsappears only when your page passed a user or an unread reply is waiting;conversation.jsdoes not. The size column shows the compressed transfer size. - Open the chat.
conversation.jsappears now, once. - Turn on Wait for the page to finish drawing in Settings, Widget and reload: the launcher now appears only after your page's largest element has painted.