Messaging Tool Frontend Implementation
The messaging tool allows managing all on-site messaging from the backend using simple frontend integrations. Banners, modals, inline article prompts, and registration walls can all be configured and targeted from the CMS — with no frontend deployments required to update your messages.
Basic Setup
Section titled “Basic Setup”Add the following script tag to every page where messaging should appear, typically in the <head> or just before </body>:
<script data-api-base="https://messaging.domain.com/" data-asset-base="https://api.domain.com/" data-site-id="1" type="text/javascript" src="https://api.domain.com/js/messaging_loader.js" async="true"></script>| Attribute | Description |
|---|---|
data-inline-selector |
CSS selector used to inject inline article messages into the page content |
data-zone-selector |
CSS selector used to specify the attribute to look for when finding zones |
data-api-base |
Base URL of the Messaging API |
data-asset-base |
Base URL for CMS API |
data-site-id |
Publication ID for the site, as configured in the CMS |
Standard templates (banners, modals, stacked panels, etc.) require no further frontend work — the loader handles rendering and basic interactions automatically.
Dev mode
Section titled “Dev mode”For developers testing the worker, you can use the following local environment URLs:
<script data-api-base="http://localhost:8787/" data-asset-base="http://localhost:8086/" data-site-id="1" type="text/javascript" src="http://localhost:8086/js/messaging_loader.js" async="true"></script>Make sure to also run Vite in messaging mode and to run the messaging worker.
Messages can be targeted to specific locations on the page using zone keys, similar to how a DFP ad unit works. Place a zone container anywhere in your HTML:
<div data-wb-zone-key="zone_name"></div>Then use that zone name when configuring the message placement in the CMS. The loader automatically discovers all zone containers on the page and includes their keys in the API request.
Custom zone attribute
Section titled “Custom zone attribute”By default the loader looks for [data-wb-zone-key]. You can change this by setting data-zone-selector on the script tag:
<script data-site-id="1" data-zone-selector="[data-my-zone]" src="https://api.domain.com/js/messaging_loader.js" async="true"></script>Inline Article Targeting
Section titled “Inline Article Targeting”Messages placed inside article content use a CSS selector to find the article body. You can specify one explicitly with data-inline-selector on the script tag. If omitted, the loader tries each of these selectors in order and uses the first match:
| Priority | Selector |
|---|---|
| 1 | [data-wb-article-body] |
| 2 | article .article-body |
| 3 | article .content-body |
| 4 | article |
| 6 | .article-texts |
The recommended approach is to add data-wb-article-body to your article body element so the selector is explicit and immune to markup changes:
<div class="article-content" data-wb-article-body>…</div>Advanced Templates
Section titled “Advanced Templates”Some templates expose interactive behaviour that depends on site-specific logic such as captcha services, authentication flows, or subscription APIs. These require a frontend event integration.
Template: Registration Wall
Section titled “Template: Registration Wall”The registration wall collects a visitor’s form input and delegates the full conversion flow — including captcha verification and the API call — to the frontend via a custom DOM event. The event contract is generic (wb:message:conversion*, not wb:message:register*), since the same wall markup can back other conversion actions besides registration (e.g. login, newsletter signup) — any template that opts into this flow fires the same events.
Events
Section titled “Events”| Event | Fired on | Detail |
|---|---|---|
wb:message:conversion |
window |
{ messageId, templateKey, zoneKey, surface, variantId, fields, resolve, reject } |
wb:message:conversion:success |
window |
{ messageId, templateKey, zoneKey, surface, variantId, fields } |
wb:message:conversion:error |
window |
{ messageId, templateKey, zoneKey, surface, variantId, fields, error } |
fields is an object built from every named <input>/<select>/<textarea> inside the message, keyed by each element’s name attribute exactly as it appears in the HTML — for the registration wall that’s { full_name, email, password }. Because the messaging tool doesn’t know in advance which fields a given template’s form has, it forwards all of them verbatim rather than picking out specific ones, so a future template with different fields (a login form with just login_email/login_password, for example) works without any JS changes here.
Because the same event fires for every template that opts into this flow, use templateKey inside your handler to decide which real-world action to perform (register, log in, subscribe to a newsletter, etc.) — see the code sample below. Today registration_wall is the only template wired to this event, but the templateKey check future-proofs your integration against additional conversion-style templates without needing a new event name for each one.
How it works
Section titled “How it works”When the visitor clicks the Register Now button, the messaging tool:
- Disables the button to prevent duplicate submissions.
- Fires
wb:message:conversiononwindowwith the collectedfieldsand aresolve/rejectpair. - Waits for the frontend to call
resolve()orreject(error).- On
resolve()→ fireswb:message:conversion:successand leaves the button disabled. - On
reject(error)→ displays the error message inside the wall, re-enables the button, and fireswb:message:conversion:error.
- On
Frontend implementation
Section titled “Frontend implementation”Listen for wb:message:conversion, branch on templateKey to identify which conversion action this is, and perform your captcha challenge and API call inside the handler:
window.addEventListener("wb:message:conversion", (event) => { const { templateKey, fields, resolve, reject } = event.detail;
if (templateKey !== "registration_wall") { // Handle other conversion-form templates here as they're introduced // (e.g. a future "login_wall" or "newsletter_wall" templateKey). return; }
const { full_name: name, email: email, password: password } = fields;
yourCaptcha .execute() .then((token) => yourApi.register(name, email, password, token)) .then(resolve) .catch(reject);});If registration fails, throw or reject with an Error whose message property contains a human-readable string — it will be shown directly to the user inside the wall:
.catch(err => reject(new Error('This email is already registered.')));Reloading on success
Section titled “Reloading on success”On a successful registration the visitor typically gains access to content, so you will usually want to reload the page:
window.addEventListener("wb:message:conversion:success", () => { window.location.reload();});If you need to do something before reloading (e.g. set a session cookie, fire an analytics event), do it before calling resolve() inside the wb:message:conversion handler rather than in the success listener, so the reload only happens once everything is confirmed.