Embed a fully customizable chat widget on your website with a single line of code.
WebChat allows you to embed a chat widget directly on your website. Visitors can interact with your flows without leaving your site, and conversations persist across page navigations and return visits.
Key Features
Creating a WebChat deployment is straightforward and doesn't require any external connections.
Click "Customize Widget" to open the visual customizer where you can configure colors, sizing, greeting messages, and more. See the Customization section for details.
After creating the deployment, click the "Embed" button to view and copy your embed code.
Add the embed code to your website, just before the closing </body> tag:
<script
async
src="https://universalchatbot.com/chatbot.js"
data-bot-url="https://universalchatbot.com/deployments/YOUR_ID/embed">
</script>
The script loads asynchronously and won't block your page from rendering. Once loaded, a chat widget button appears in the corner of your page (bottom-right by default).
Configuration in Embed Code
All widget configuration is embedded directly in the script tag as data attributes. When you customize your widget in the dashboard, the embed code automatically includes all your settings.
Where the conversation appears on the page. Set it under Widget → Mode in the widget customizer; the embed code updates to match.
Launcher (default)InlineInline needs to know where to render, so the embed code includes a container and points at it with data-target:
<div id="universal-chatbot"></div>
<script
async
src="https://universalchatbot.com/chatbot.js"
data-bot-url="https://universalchatbot.com/deployments/YOUR_ID/embed"
data-mode="inline"
data-target="#universal-chatbot">
</script>
Point data-target at any selector you like if you would rather place it yourself. The container needs no height: a card layout sizes the widget to the question on screen, so the element grows and shrinks as the conversation moves, and a chat layout uses the widget height from the customizer. Style the container for width, spacing and anything else you want around it.
Inline drops the widget's own chrome: no header, no launcher, no panel background, border, corners or shadow. Your container owns the frame, so style it to match the rest of your page. The header settings in the customizer (title, subtitle and the theme toggle) only apply to the launcher.
Inline waits for the visitor, because a conversation is a real thing on your account: it lands in your inbox for someone to read, and may count towards your usage. A page that inline sits on is loaded by everyone who visits, most of whom will scroll past, and you should not be paying for those.
How it waits depends on the layout. A card shows a button, labelled with your greeting. A chat shows its greeting above the message box and starts when the visitor writes, so typing is all it takes.
If you would rather it opened with the first question already asked, say so:
data-autostart="true"
A launcher ignores this and always starts, because opening it was the visitor asking.
A flow can define entry points on its Start node: the ways in, each with its own path. On channels where a link can pre-fill a message they are matched against whatever the visitor arrives saying. On webchat there is no such link, so the widget offers them instead.
Picking one sends it as the first message, exactly as a campaign link would, so the flow branches the same way on every channel. Nothing is created until someone picks.
Entry points come before autostart. A flow that asks how to begin has to be allowed to ask it, so where they exist the widget always offers them, including in a launcher. That also means a launcher stops creating a conversation merely because someone opened it.
Start Over returns to the entry points too, since starting again is a new conversation and it should begin the way the first one did.
Mode and layout are separate choices
Mode is where the conversation sits on your page. Layout, below, is how it presents the flow. Any combination works: a card inline for a form on a landing page, or a card in the launcher for a short intake in the corner.
A webchat deployment presents its flow in one of two layouts. Set it under Widget → Layout in the widget customizer.
Chat (default)CardCard layout suits a form, not a chat
On a form somebody is trying to finish, waiting for each message to arrive costs completions. The card shows the whole question at once and moves on as soon as it is answered. A visitor who leaves partway through picks up where they stopped when they come back.
Mode and layout are set independently, so every pairing is available. What each one is like to meet:
| Combination | Starts | Height |
|---|---|---|
| Launcher + chat The familiar corner widget. |
On open | Widget height |
| Launcher + card A short intake in the corner. |
On open | Widget height |
| Inline + card A form in the page. Shows a button labelled with your greeting. |
On the button | Follows the question |
| Inline + chat A conversation in the page. Shows your greeting above the message box. |
When the visitor writes | Widget height |
Only a card knows how tall it is, because it shows one question at a time. A chat is a scrolling thread with no natural end, so inline it takes the widget height from the customizer and your container should leave room for it.
The widget customizer provides a visual interface to configure every aspect of your chat widget's appearance and behavior.
Testing a draft flow uses its own widget settings, separate from any deployment. Open Test in the flow editor and use the palette button to change them, so you can try a layout or a colour scheme before committing it to a deployment, or before the flow is deployed at all.
The widget supports both light and dark themes with automatic detection and full color customization.
lightdarksystem*tailwind_class** Reactive: automatically updates when value changes
Start with one of our professionally designed presets:
Each theme (light and dark) has its own set of customizable colors:
primaryColorprimaryHoverColoraccentColorheaderColorlauncherColorbotMessageBackgroundColorbotMessageTextColorbotMessageTextSecondaryColoruserMessageBackgroundColoruserMessageTextColoruserMessageTextSecondaryColorbackgroundColorsurfaceColorsurfaceTextColorborderColorFont FamilyGeorgia, serif if you are not naming a Google font.Font SizeBorder RadiusModeLayoutTitleSubtitleWidget WidthWidget HeightButton SizePositionShadow StrengthBackdrop BlurAnimationsThe greeting is how a visitor learns what the conversation is for before they commit to it. Every mode uses the same sentence, in the way that mode has to say it:
Write it as an invitation and it works in all three: See how it qualifies a lead reads as well on a button as it does in a bubble. Leave it empty and the card falls back to "Start" and the inline chat opens on an empty panel.
Greeting MessageGreeting DelayShow GreetingOptionally collect user information before or during the conversation. This data becomes available as variables in your flow.
neverbefore_chatafter_first_messageSelect which fields to collect:
Each field can be marked as required or optional. Users can skip optional fields.
Accessing Collected Info
Collected user information is available in your flow via {{@request.user.name}}, {{@request.user.email}}, and {{@request.user.phone}}.
Control the widget programmatically using the global UCBChatbotWidget object. This is how you open the chat from your own button, link or menu item rather than the floating launcher.
UCBChatbotWidget.open()UCBChatbotWidget.close()UCBChatbotWidget.toggle()UCBChatbotWidget.isOpen()UCBChatbotWidget.setUnreadCount(n)// Open chat when user clicks a custom button
document.getElementById('help-button').addEventListener('click', () => {
UCBChatbotWidget.open();
});
// Open chat automatically after 30 seconds
setTimeout(() => {
if (!UCBChatbotWidget.isOpen()) {
UCBChatbotWidget.open();
}
}, 30000);
// Open as soon as the widget exists, e.g. arriving from a "chat with us" link
function whenChatReady(callback) {
if (window.UCBChatbotWidget) return callback(window.UCBChatbotWidget);
const timer = setInterval(() => {
if (window.UCBChatbotWidget) {
clearInterval(timer);
callback(window.UCBChatbotWidget);
}
}, 100);
}
if (location.hash === '#chat') {
whenChatReady((widget) => widget.open());
}
UCBChatbotWidget points at whichever widget loaded last, which is all you need when the page carries one bot. Where a page runs two, reach for them by the bot URL you embedded them with.
// Keyed by the same URL as data-bot-url on the script tag
const sales = UCBChatbotWidgets['https://universalchatbot.com/deployments/YOUR_ID/embed'];
sales.open();
A widget is removed from both handles when it is taken off the page, so anything you hold on to should be read fresh rather than stored.
Wait for Widget Ready
The embed script is async, so UCBChatbotWidget does not exist the moment your page runs.
Calling it from a click handler or a timer is safe, because the widget has loaded by then. Calling it directly
during page load will fail. To open the chat straight away, poll for it as the last example above shows.