Display notification bubble
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Webchat Notification Bubble</title>
<script src="https://cdn.botpress.cloud/webchat/v3.2/inject.js"></script>
<script src="https://files.bpcontent.cloud/xxxx/xx/xx/xx/xxxxxxxxxxxxx-xxxxxxxx.js" defer></script>
<script src="index.js" defer></script>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<!-- Notification Bubble -->
<div id="msgDiv" class="cb tri-right btm-right" role="button" tabindex="0" aria-live="polite" aria-label="Notification" style="display:none">
<span id="msgText"></span>
<button class="close-chip" type="button" aria-label="Dismiss">×</button>
</div>
</body>
</html>function openNotification() {
if (window.botpress) {
window.botpress.open();
} else {
alert('Botpress integration not available.');
}
}
(function () {
const msgDiv = document.getElementById('msgDiv');
const msgText = document.getElementById('msgText');
const closeBtn = msgDiv?.querySelector('.close-chip');
function showBubble(text) {
if (!msgDiv || !msgText) return;
msgText.textContent = text || '';
msgDiv.style.display = 'block';
msgDiv.onclick = () => openNotification();
msgDiv.focus();
msgDiv.onkeydown = (e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
openNotification();
}
};
window.botpress.on('webchat:opened', () => {
if (msgDiv) msgDiv.style.display = 'none';
});
setTimeout(() => (msgDiv.style.display = 'none'), 15000);
}
if (closeBtn) {
closeBtn.addEventListener('click', (e) => {
e.stopPropagation();
msgDiv.style.display = 'none';
});
}
function extractPayload(raw) {
let data = raw;
if (typeof data === 'string') {
try { data = JSON.parse(data); } catch { /* ignore */ }
}
if (data && typeof data.event === 'string') {
try { data = JSON.parse(data.event); } catch { data = { eventType: data.eventType, ...data }; }
} else if (data && typeof data.event === 'object') {
data = data.event;
}
return data || {};
}
if (window.botpress?.on) {
window.botpress.on('customEvent', (payload) => {
const webchat = document.querySelector('iframe[title="Botpress"]');
const data = extractPayload(payload);
if (data?.eventType === 'notification' && webchat && webchat.classList.contains('bpClose')) {
showBubble(data.message);
}
});
}
})();:root {
/* Change these to match your page's brand */
--bubble-bg: #ccddfa; /* background */
--bubble-fg: #173569; /* text */
}
.cb {
position: fixed;
right: 24px;
bottom: 102px;
z-index: 9999;
display: none; /* shown by JS */
background: var(--bubble-bg);
color: var(--bubble-fg);
border-radius: 16px;
padding: 14px 42px 14px 16px;
max-width: 340px;
line-height: 1.35;
box-shadow: 0 10px 25px rgba(0, 0, 0, .18);
cursor: pointer;
font-family: Roboto, system-ui, -apple-system, Segoe UI, Roboto, "Helvetica Neue", Arial;
font-size: 15px;
transition: transform .2s ease, box-shadow .2s ease, opacity .2s ease;
animation: bubble-in .28s cubic-bezier(.2, .8, .2, 1) both;
}
.cb:hover {
transform: translateY(-1px);
box-shadow: 0 12px 30px rgba(0, 0, 0, .22);
}
.cb:focus {
outline: none;
}
/* Chat tail */
.tri-right.btm-right::after {
content: "";
position: absolute;
right: 22px;
bottom: -8px;
width: 16px;
height: 16px;
background: var(--bubble-bg);
transform: rotate(45deg);
box-shadow: 0 10px 25px rgba(0, 0, 0, .18);
border-bottom-right-radius: 4px;
}
/* Close chip (optional, inside the bubble) */
.cb .close-chip {
position: absolute;
top: 8px;
right: 8px;
width: 26px;
height: 26px;
border-radius: 999px;
background: rgba(255, 255, 255, .18);
color: var(--bubble-fg);
border: 0;
cursor: pointer;
line-height: 1;
font-size: 16px;
}
.cb .close-chip:hover {
background: rgba(255, 255, 255, .28);
}
@keyframes bubble-in {
from { transform: translateY(10px) scale(.98); opacity: 0; }
to { transform: translateY(0) scale(1); opacity: 1; }
} You can make your website display a notification bubble when your bot sends a new message.
The notification bubble is only displayed if the Webchat window is closed when you receive the message. You can then:
- Open the notification (by clicking it or pressing Enter / Space)
- Dismiss the notification (by clicking ×)
- Open Webchat using the FAB (which dismisses the notification automatically)
You will need:
- A published bot
- Familiarity with HTML, CSS and JavaScript
Since Webchat uses Markdown for rich text in messages, some messages may not render properly in the notification bubble without further processing.
Step 1: Add a Hook to your bot
- In the Studio, navigate to the Hooks section.
- Select Create Hook and set its type to Before Outgoing.
- Paste the following code into the Hook:
await actions.webchat.customEvent({
conversationId: event.conversationId,
event: JSON.stringify({
eventType: 'notification',
message: outgoingEvent.preview,
}),
})
This Hook notifies your website every time your bot sends a new message—that way, your website can display a notification bubble.
Step 2: Configure your website
Now, you can configure your website to display the notification bubble.
Add the bubble HTML element
First, add the bubble element somewhere in your website’s HTML:
<!-- Notification Bubble -->
<div
id="msgDiv"
class="cb tri-right btm-right"
role="button"
tabindex="0"
aria-live="polite"
aria-label="Notification"
style="display:none"
>
<span id="msgText"></span>
<button class="close-chip" type="button" aria-label="Dismiss">×</button>
</div>
Add JavaScript
Then, add the following snippet to your website’s JavaScript:
function openNotification() {
if (window.botpress) {
window.botpress.open()
} else {
alert('Botpress integration not available.')
}
}
;(function () {
const msgDiv = document.getElementById('msgDiv')
const msgText = document.getElementById('msgText')
const closeBtn = msgDiv?.querySelector('.close-chip')
function showBubble(text) {
if (!msgDiv || !msgText) return
// Display the notification bubble
msgText.textContent = text || ''
msgDiv.style.display = 'block'
// Avoid stacking handlers on repeated events
msgDiv.onclick = () => openNotification()
// Keyboard access
msgDiv.focus()
msgDiv.onkeydown = (e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault()
openNotification()
}
}
// Hide the notification bubble if Webchat is opened
window.botpress.on('webchat:opened', () => {
if (msgDiv) msgDiv.style.display = 'none'
})
// Optional auto-hide after a period
setTimeout(() => (msgDiv.style.display = 'none'), 15000)
}
// Close/dismiss without action
if (closeBtn) {
closeBtn.addEventListener('click', (e) => {
e.stopPropagation()
msgDiv.style.display = 'none'
})
}
// Extract payload from event
function extractPayload(raw) {
let data = raw
// If the data is a string
if (typeof data === 'string') {
try {
data = JSON.parse(data)
} catch {
/* ignore */
}
}
// Handle both strings and objects as event types
if (data && typeof data.event === 'string') {
try {
data = JSON.parse(data.event)
} catch {
data = { eventType: data.eventType, ...data }
}
} else if (data && typeof data.event === 'object') {
data = data.event
}
return data || {}
}
if (window.botpress?.on) {
// Listen for custom events from the bot
window.botpress.on('customEvent', (payload) => {
const webchat = document.querySelector('iframe[title="Botpress"]')
const data = extractPayload(payload)
// Show the notification bubble if the Webchat window is closed
if (data?.eventType === 'notification' && webchat && webchat.classList.contains('bpClose')) {
showBubble(data.message)
}
})
}
})()
This handles the behaviour of the notification bubble. For example:
- Displays the message received from the event’s payload
- Remains visible for 15 seconds before automatically closing
- Can be selected by clicking or pressing Enter / Space
Add CSS
Finally, add this CSS snippet to your website’s stylesheet:
:root {
/* Change these to match your page's brand */
--bubble-bg: #ccddfa; /* background */
--bubble-fg: #173569; /* text */
}
.cb {
position: fixed;
right: 24px;
bottom: 102px;
z-index: 9999;
display: none; /* shown by JS */
background: var(--bubble-bg);
color: var(--bubble-fg);
border-radius: 16px;
padding: 14px 42px 14px 16px;
max-width: 340px;
line-height: 1.35;
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.18);
cursor: pointer;
font-family:
Roboto,
system-ui,
-apple-system,
Segoe UI,
Roboto,
'Helvetica Neue',
Arial;
font-size: 15px;
transition:
transform 0.2s ease,
box-shadow 0.2s ease,
opacity 0.2s ease;
animation: bubble-in 0.28s cubic-bezier(0.2, 0.8, 0.2, 1) both;
}
.cb:hover {
transform: translateY(-1px);
box-shadow: 0 12px 30px rgba(0, 0, 0, 0.22);
}
.cb:focus {
outline: none;
}
/* Chat tail */
.tri-right.btm-right::after {
content: '';
position: absolute;
right: 22px;
bottom: -8px;
width: 16px;
height: 16px;
background: var(--bubble-bg);
transform: rotate(45deg);
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.18);
border-bottom-right-radius: 4px;
}
/* Close chip (optional, inside the bubble) */
.cb .close-chip {
position: absolute;
top: 8px;
right: 8px;
width: 26px;
height: 26px;
border-radius: 999px;
background: rgba(255, 255, 255, 0.18);
color: var(--bubble-fg);
border: 0;
cursor: pointer;
line-height: 1;
font-size: 16px;
}
.cb .close-chip:hover {
background: rgba(255, 255, 255, 0.28);
}
@keyframes bubble-in {
from {
transform: translateY(10px) scale(0.98);
opacity: 0;
}
to {
transform: translateY(0) scale(1);
opacity: 1;
}
}
You can modify the styling as needed.