@custral/widget is the Custral chat widget as a React component, plus the
headless engine underneath it. It uses a publishable key (pk_…), which is
safe to ship to the browser.
No bundler, or not a React app? Load the standalone
widget.js script instead. It is this same
package, built as a self-contained bundle with React inlined.Install
The widget
One component, mounted once, anywhere in your tree. It renders its own bubble and panel and positions itself.string
required
Your publishable key (
pk_…), from Settings → Applications.string
Target a specific widget channel. Defaults to the workspace’s first.
string
Defaults to
https://api.custral.com.boolean
Force dark or light. Omitted, the widget reads the host page.
{name?, email?, phone?}
Pre-fill who the visitor is. Skips the intake form.
string
A token minted by your server that vouches for who this visitor is. See
below.
Identifying a visitor
visitor is convenient and self-reported: it fills the form in, and the person
on the other end typed it. That is fine for lead capture and not fine for
anything that has to be right.
identityToken is the version the product will trust. Your server mints it with
its secret key, so the claim comes from you rather than from the browser:
Building your own UI
The component is assembled from parts the package also exports, so you can replace the shell and keep the machinery.useCustralChat owns the conversation: init, socket, messages, typing,
intake, conversation list, booking. ChatBubble, ChatPanel, ChatHeader,
ChatMessageList, ChatMessage, ChatInput, IntakeForm, ConversationList
and SchedulePicker are the pieces it drives.
For no React at all, CustralChat is the engine on its own:
Construct a headless client.
Loads widget config, restores any prior visitor from
localStorage, and
returns the resolved config (or null on failure).Registers a callback for agent messages pushed in real time.
Sends a visitor message, creating a conversation on the first send.
Attaches self-reported contact info for lead capture.
Tears down the socket and in-memory session. Does not clear
localStorage.Troubleshooting
CustralChat: publishableKey is required / init returns null
CustralChat: publishableKey is required / init returns null
The widget needs a publishable key (
pk_…), not a secret key. Create one in
Settings → Applications. If init() returns null, the /widget/init
call failed: check the browser console and confirm the key and channelId.No real-time messages arrive
No real-time messages arrive
Delivery uses a WebSocket to
/widget. If your environment blocks
WebSockets, history still loads on demand, but pushed agent replies will not
arrive. Look for Socket connected in the console.Hooks throw, or the widget renders blank
Hooks throw, or the widget renders blank
Two copies of React in one page. React and React DOM are peers here for that
reason; check your bundler is deduping them.
I was importing from @custral/ui or @repo/widget
I was importing from @custral/ui or @repo/widget
CustralChat and the components are all in @custral/widget now. Change the
import and nothing else.