Embedding in a container
First include the loader script. The embedded mode places chat in your website container; popup shows a separate window. button_only is incompatible with embedded.
Embed the Widget in a Container
This step applies only to embedded mode. The container must have actual dimensions: the widget occupies 100% of its width and height. theme.width and theme.height size the popup window and do not replace the container dimensions.
<div id="senler-widget" style="width: 100%; height: 600px"></div>
<!-- Load the loader script from the generated channel code before this block. -->
<script>
SenlerWidget.init({
channel_id: "xxx",
display_mode: "embedded",
container: "#senler-widget",
theme: {
border_radius: 18,
},
features: {
element_selection: true,
},
});
</script>
This example places chat in #senler-widget and enables page element selection. Take the loader URL and real channel_id from the embed code in the cabinet. Only include an authenticated visitor's data together with a server signature.
Widget Inside a Hideable Panel
If your site hides an embedded widget inside its own panel or tab, report whether the visitor can see the chat. For an initially hidden panel, pass surfaceVisible: false during init, then update it whenever the panel is shown or hidden:
<button onclick="setChatPanelVisible(true)">Open chat</button>
<div id="chat-panel" hidden>
<button onclick="setChatPanelVisible(false)">Close chat</button>
<div id="senler-widget" style="height: 600px"></div>
</div>
<script>
// Load the script from the channel's embed code before this example.
SenlerWidget.init({
channel_id: "xxx",
display_mode: "embedded",
container: "#senler-widget",
surfaceVisible: false,
});
function setChatPanelVisible(visible) {
document.getElementById("chat-panel").hidden = !visible;
SenlerWidget.updateRuntime({ surfaceVisible: visible });
}
</script>
surfaceVisible controls read receipts, not layout: it does not hide or open the container on its own. Messages are not marked as read inside a hidden panel or a background browser tab. Authentication, explicitly sent requests, and WebMCP continue to work. Built-in popup opening and closing, as well as open() and close() calls, are handled automatically.
In React, pass current visibility through runtime={{ surfaceVisible: panelIsVisible }}; there is no need to change config or recreate the widget. React integration example.
Collapsing an embedded widget
Popup mode already has a close button in its header. It hides the window, and the floating button reopens the same instance.
In embedded mode, enable shell.collapse_button: true when the user needs a collapse button in the header. The button only tells the site that the user wants to collapse the widget; the site calls SenlerWidget.close() or closes its outer panel.
SenlerWidget.init({
channel_id: "xxx",
display_mode: "embedded",
container: "#senler-widget",
shell: {
collapse_button: true,
},
onCollapse(detail) {
console.log("The user requested collapse", detail);
SenlerWidget.close();
},
});
Instead of onCollapse, subscribe once to the event:
window.addEventListener("senler-widget:collapse-request", (event) => {
if (event.detail.display_mode === "embedded") {
SenlerWidget.close();
}
});
detail contains channel_id and display_mode. If both the callback and event listener are configured, both handlers run. Normally choose one to avoid collapsing twice.
CLOSE_WIDGET and COLLAPSE_WIDGET are internal iframe protocol messages. The site must not send them through postMessage.
Mobile gesture
shell.mobile_edge_swipe: true enables a left-edge swipe inside the widget. The loader does not close the interface; it dispatches senler-widget:mobile-edge-swipe.
window.addEventListener("senler-widget:mobile-edge-swipe", (event) => {
if (event.detail.side === "left") {
closeMobilePanel();
}
});
detail contains channel_id, display_mode, and side: "left". This is a navigation signal for the site, not a replacement for the collapse button.
Use SenlerWidget.close() for ordinary hiding: it also hides the embedded wrapper while preserving state. SenlerWidget.open() shows the same instance. SenlerWidget.destroy() removes the widget and requires another init afterwards. Method reference.