Installing the BloomingBox shopping assistant
This page is for BloomingBox's web developers. It covers everything needed to add the chat widget to the Next.js storefront. Allow about 30 minutes for the script tag, or half a day for the React component with a shared cart and sign-in.
Choose an install
| Option | Effort | You get | | --- | --- | --- | | Script tag | Paste two lines | The assistant on every page; its own cart and guest checkout | | React component (npm) | Add a component and three callbacks | Shared cart with the site, sign-in handoff, city kept in sync |
Both render inside a Shadow DOM, so the site's CSS and the widget's CSS never affect each other.
Option 1: script tag
Add this once, for example in the root layout:
<script src="https://chat-cdn.<domain>/v1/bb-chat.js" defer></script>
<bloomingbox-chat locale="en" country="ae" city="dubai" agent-url="https://chat-api.<domain>"></bloomingbox-chat>
In Next.js:
import Script from "next/script";
<Script src="https://chat-cdn.<domain>/v1/bb-chat.js" strategy="lazyOnload" />
<bloomingbox-chat locale="en" country="ae" city="dubai" agent-url="https://chat-api.<domain>" />
The script is about 1.5 kB gzipped and does no work until a customer opens the chat. The rest (about 90 kB) loads on first open.
Element attributes
| Attribute | Values | Default |
| --- | --- | --- |
| locale | en, ar | en |
| country | ae, sa | ae |
| city | City code from the "Deliver To" selector, e.g. dubai (not DXB) | dubai |
| agent-url | Chat API base URL | required |
| position | bottom-right, bottom-left | bottom-right |
| offset-bottom | Pixels from the bottom; the default clears the WhatsApp button | 96 |
Changing city on the element updates the assistant.
Option 2: React component
pnpm add @looqup/bloomingbox-chat
Render it client-side only:
"use client";
import dynamic from "next/dynamic";
import { useRef } from "react";
import type { BloomingBoxChatHandle } from "@looqup/bloomingbox-chat";
const BloomingBoxChat = dynamic(() => import("@looqup/bloomingbox-chat").then((m) => m.BloomingBoxChat), { ssr: false });
export function Assistant({ city, locale, cartId, token, openLogin, refreshCart, setCity }) {
const chat = useRef<BloomingBoxChatHandle>(null);
// After your login completes: chat.current?.setCustomerToken(newToken)
return (
<BloomingBoxChat
ref={chat}
agentUrl="https://chat-api.<domain>"
locale={locale}
country="ae"
city={city}
getCustomerToken={() => token}
getCartId={() => cartId}
onCartChange={(id, count) => refreshCart(id, count)}
onSignInRequested={openLogin}
onCityChange={setCity}
/>
);
}
Options
| Option | Type | What it does |
| --- | --- | --- |
| agentUrl | string | Chat API base URL |
| locale, country, city | string | As for the element attributes |
| getCustomerToken | () => string \| null | The signed-in customer's BloomingBox token, read when the chat opens. It is stored encrypted on the server for 24 hours and never shown to the AI model. |
| getCartId | () => string \| null | The site's cart id, so chat and site share one cart |
| onCartChange | (cartId, count) => void | Called when the chat changes the cart: refresh your cart icon |
| onSignInRequested | (mode: "login" \| "register") => void | At checkout, a customer who isn't signed in tapped Log in or Register in the chat: open your login or sign-up. When it finishes, call setCustomerToken(token). The chat checks the token with BloomingBox and shows "Signed in as …" at the top. Customers can also continue as a guest. |
| onCityChange | (city) => void | The customer changed the city in the chat: update your "Deliver To" selector |
| position, offset | | Placement |
Script-tag API (no React)
BloomingBoxChat.init({ agentUrl, locale: "en", country: "ae", city: "dubai",
getCustomerToken, getCartId, onCartChange, onSignInRequested, onCityChange });
BloomingBoxChat.setCustomerToken(token); // after login
BloomingBoxChat.open(); // e.g. from a "Chat with us" link
Events on the element
| Event | detail |
| --- | --- |
| bb:open | none |
| bb:cart-change | { cartId, count } |
| bb:sign-in-requested | { mode: "login" \| "register" } |
| bb:city-change | { city } |
Content-Security-Policy
Add these sources to the storefront's CSP. The /csp route on this demo runs with exactly this policy.
script-src https://chat-cdn.<domain>
connect-src https://chat-api.<domain>
frame-src https://*.noonpayments.com https://chat-api.<domain>
img-src data: https://s3.me-central-1.amazonaws.com https://s3.ap-southeast-1.amazonaws.com
font-src data: https://fonts.gstatic.com
style-src https://fonts.googleapis.com
If you use nonces with 'strict-dynamic', put the nonce on the bb-chat.js script tag; the app chunk it loads is then trusted automatically.
What the widget never does
- It never asks for card numbers, CVV, passwords or one-time codes. Customers pay on Noon Payments' hosted page and sign in on bloomingbox.com.
- It never reads cookies or storage from the host page. It keeps one item in
localStorage: the privacy notice acknowledgement.
Styling and placement
The launcher sits 96 px above the bottom-right corner, so it clears the WhatsApp button. On screens narrower than 640 px the chat opens as a full-screen sheet. Colours, type and logo follow the BloomingBox brand guidelines: a Carbon Black header with the gold logo, Poppins for text, Magnolia for display titles, Cairo for Arabic, Sand and Carbon pill buttons and the Peach accent. The logo and Magnolia are built into the widget (data: URLs), which is why img-src and font-src include data:.
Accessibility
The panel traps focus while open, Esc closes it, new messages are announced politely to screen readers, and every control works from the keyboard. Arabic switches the whole panel to right-to-left.
Troubleshooting
| Symptom | Check |
| --- | --- |
| Launcher does not appear | The script URL loads (no 404) and script-src allows it; the element exists in the page |
| Opens, then "Something went wrong" | connect-src allows agent-url; the browser console shows the request to /v1/session |
| Chat cannot keep a session | Third-party cookies: the chat API must be on a subdomain of bloomingbox.com in production |
| Cart icon doesn't update | onCartChange is wired (React) or you listen for bb:cart-change (script tag) |
| Sign-in never completes | Call setCustomerToken(token) after your login succeeds |
| Arabic shows left to right | Set locale="ar"; the widget sets its own direction independently of the page |
| Wrong city's products | Pass the city code (dubai, abudhabi), not the display name or DXB |
Questions: contact the Looqup integration team.