Commanders Act Integration

Inject the Ekoo widget through a custom tag in TagCommander

Commanders Act (formerly TagCommander) is a European tag management platform. Like Google Tag Manager, it lets you add the Ekoo widget to your product pages without touching your site's source code. You create a Free input (custom) tag that inserts the container and loads the Ekoo script.

The widget auto-loads its config from the backoffice. Appearance attributes are optional overrides — no need to set them if the backoffice is configured.
Core attributes
data-ekooReq
string
Your Ekoo website UUID. Found in the backoffice → Site settings.
data-ekoo-product-idReq
string
Product reference (must match exactly what is in your Ekoo catalog).
data-ekoo-locale
string·default: auto
Locale code: fr, en, es, it, de, ar, cn, tw, hk, jp, kr, nl, tr, pl, pt, lu, be, ru. "auto" = navigator.language detection.
data-ekoo-variant
string
Stable reference of a widget configuration. The product, its audios, and its reviewers still come from data-ekoo-product-id; only the widget styling changes.
data-ekoo-review-id
string
ID of a specific audio review. If omitted, the first published review is used.
data-ekoo-on-event
string (fn name)
Name of a global window function called on each widget event (printed, played-0, played-25…).
Appearance — backoffice overrides
data-ekoo-direction
normal | reverse·default: normal
Expansion direction. normal = left-to-right, reverse = right-to-left.
data-ekoo-scale
number·default: 1
Scale factor (e.g. "1.2" for 20% larger).
data-ekoo-animation
string·default: pulse
Animation type for the widget icon at rest.
data-ekoo-animation-duration
string·default: continuous
Animation duration.
data-ekoo-always-open
boolean·default: false
If "true", the widget stays permanently expanded.
data-ekoo-show-image
boolean·default: true
Show or hide the product image in the widget.
data-ekoo-not-fully-clickable
boolean·default: false
If "true", only the play button is clickable.
data-ekoo-autoplay
boolean·default: false
Automatically start audio playback on load.
data-ekoo-show-transcript
boolean·default: false
Show a button to read the audio transcript.
data-ekoo-show-speed-button
boolean·default: false
Show a playback speed control.
data-ekoo-closed-state-main-text
string
Main CTA text shown when the widget is collapsed.
data-ekoo-closed-state-secondary-text
string
Secondary text below the CTA when the widget is collapsed.
SPA & Shadow DOM
data-ekoo-mode
spa | static·default: auto
Forces rendering mode. Auto-detected (Next.js, Nuxt, React, Vue, Angular, Sapper). Only use if auto-detection fails.
data-shadow-mode
open | closed·default: open
"open" (default) enables inspection, external CSS access and analytics tracking. Set to "closed" to fully isolate the widget.
Global JS config
window.EKOO_FORCE_SPA = true
Forces SPA mode globally (alternative to data-ekoo-mode="spa" on each widget).
window.ekooShadowMode = "open"
Global Shadow DOM mode (alternative to data-shadow-mode on each widget).

1. Create a Custom Tag

  1. Open your TagCommander container.
  2. Go to the EDIT step and click Add a tag.
  3. Pick the Free input (custom) tag from the library.
  4. In the JAVASCRIPT CODE section, paste the snippet below (replacing the default content).
  5. Uncheck Use Tag Cleaner if you want to keep the code as-is.
Custom tag — Commanders Act
html
1<script>
2(function() {
3 // 1. Create the widget container
4 var container = document.createElement('div');
5 container.setAttribute('data-ekoo', 'YOUR_WEBSITE_ID');
6 container.setAttribute('data-ekoo-product-id', 'MY_PRODUCT_ID');
7 container.setAttribute('data-ekoo-locale', 'fr');
8
9 // 2. Insert the container at the desired location
10 // Adjust the CSS selector to match your site
11 var target = document.querySelector('.product-detail');
12 if (target) {
13 target.appendChild(container);
14 } else {
15 document.body.appendChild(container);
16 }
17
18 // 3. Load the Ekoo script (only once per page)
19 if (!document.querySelector('script[src*="widget-4.0.0-standalone"]')) {
20 var script = document.createElement('script');
21 script.src = 'https://app.ekoo.co/widgets/widget-4.0.0-standalone.js';
22 script.defer = true;
23 document.head.appendChild(script);
24 } else if (window.ekooLoad) {
25 window.ekooLoad();
26 }
27})();
28</script>

2. Target the Right Element on the Page

Use document.querySelector() to position the widget where you want it. Common selectors:

  • .product-detail — main product detail block
  • #add-to-cart — right before/after the add-to-cart button
  • [data-product-id] — element carrying the product ID
💡

Tip

Inspect your product page in the browser, identify the ideal CSS selector, then test it in the console with document.querySelector('...').

3. Dynamic Product ID via tc_vars

Commanders Act exposes your business data through the global window.tc_vars object (the Commanders Act dataLayer). On a product page, you'll typically find tc_vars.product_id or tc_vars.product_array.

Dynamic product ID from tc_vars
html
1<script>
2(function() {
3 // Read the product ID from the Commanders Act dataLayer (tc_vars)
4 // Common conventions: tc_vars.product_id or tc_vars.product_array[0].id
5 var tcVars = window.tc_vars || {};
6 var productId =
7 tcVars.product_id ||
8 (Array.isArray(tcVars.product_array) && tcVars.product_array[0]
9 ? tcVars.product_array[0].id
10 : null);
11
12 if (!productId) return; // Not a product page → do nothing
13
14 var container = document.createElement('div');
15 container.setAttribute('data-ekoo', 'YOUR_WEBSITE_ID');
16 container.setAttribute('data-ekoo-product-id', productId);
17 container.setAttribute('data-ekoo-locale', tcVars.env_language || 'fr');
18
19 var target = document.querySelector('.product-detail');
20 if (target) target.appendChild(container);
21
22 if (!document.querySelector('script[src*="widget-4.0.0-standalone"]')) {
23 var script = document.createElement('script');
24 script.src = 'https://app.ekoo.co/widgets/widget-4.0.0-standalone.js';
25 script.defer = true;
26 document.head.appendChild(script);
27 } else if (window.ekooLoad) {
28 window.ekooLoad();
29 }
30})();
31</script>

Populate tc_vars before the container

The tc_vars object must be populated before the container file loads. Otherwise the tag fires with an empty product_id and the widget won't mount.

4. Configure the Trigger

The tag should fire only on product pages, after the DOM is ready. Three common strategies in Commanders Act:

  • Page View + condition — a page_view trigger filtered by tc_vars.page_type === 'product'.
  • URL contains — filter on the page URL (e.g. /product/).
  • Custom event — fire via cact('trigger', 'product_view', {...}) (particularly useful for SPAs, see section 5).

5. SPA Sites — Event-Driven Triggers

On a SPA (React, Vue, Angular…), navigation doesn't reload the page — a Page View trigger only fires once. Use the Commanders Act event API instead:

Trigger via cact() — SPA
html
1<script>
2// Listen to a custom Commanders Act event fired by your site
3// (e.g. cact('trigger', 'product_view', { product_id: '...' }))
4window.cact = window.cact || function () {
5 (window.cact.q = window.cact.q || []).push(arguments);
6};
7
8cact('on', 'product_view', function (data) {
9 if (!data || !data.product_id) return;
10
11 var container = document.createElement('div');
12 container.setAttribute('data-ekoo', 'YOUR_WEBSITE_ID');
13 container.setAttribute('data-ekoo-product-id', data.product_id);
14 container.setAttribute('data-ekoo-locale', data.locale || 'fr');
15
16 var target = document.querySelector('.product-detail') || document.body;
17 target.appendChild(container);
18
19 if (!document.querySelector('script[src*="widget-4.0.0-standalone"]')) {
20 var script = document.createElement('script');
21 script.src = 'https://app.ekoo.co/widgets/widget-4.0.0-standalone.js';
22 script.defer = true;
23 document.head.appendChild(script);
24 } else if (window.ekooReload) {
25 window.ekooReload();
26 }
27});
28</script>

On the application side, your SPA code must emit the event on each product navigation:

1// On every navigation to a product page
2cact('trigger', 'product_view', {
3 product_id: 'my-product-123',
4 locale: 'en',
5});

6. Target Element Not Yet Rendered

⚠️

The tag runs before client rendering

If your site renders the product block client-side (lazy-loading, deferred hydration), the target element may not exist when the tag runs. Use a MutationObserver to wait for it to appear.

MutationObserver — wait for the target element
html
1<script>
2(function() {
3 function injectEkoo(target) {
4 var container = document.createElement('div');
5 container.setAttribute('data-ekoo', 'YOUR_WEBSITE_ID');
6 container.setAttribute('data-ekoo-product-id',
7 (window.tc_vars && window.tc_vars.product_id) || 'MY_PRODUCT_ID');
8 container.setAttribute('data-ekoo-locale', 'fr');
9 target.appendChild(container);
10
11 if (!document.querySelector('script[src*="widget-4.0.0-standalone"]')) {
12 var script = document.createElement('script');
13 script.src = 'https://app.ekoo.co/widgets/widget-4.0.0-standalone.js';
14 script.defer = true;
15 document.head.appendChild(script);
16 } else if (window.ekooLoad) {
17 window.ekooLoad();
18 }
19 }
20
21 var target = document.querySelector('.product-detail');
22 if (target) {
23 injectEkoo(target);
24 return;
25 }
26
27 var observer = new MutationObserver(function(mutations, obs) {
28 var el = document.querySelector('.product-detail');
29 if (el) {
30 obs.disconnect();
31 injectEkoo(el);
32 }
33 });
34 observer.observe(document.body, { childList: true, subtree: true });
35})();
36</script>

7. Preview and Publish

  1. Enable Commanders Act Debug mode (URL parameter ?tc_debug=1 or via the browser extension).
  2. Navigate to a product page that has a published audio.
  3. In the Commanders Act console, verify that the tag fires and that the Ekoo widget renders.
  4. If everything looks good, deploy the container to production.

Consent (TrustCommander)

The Ekoo widget uses no cookies and collects no personal data on the client side. It therefore does not require GDPR consent to run. You can fire it as soon as the page loads, without waiting for CMP acceptance.

ℹ️

If your internal policy requires it

If you still prefer to gate loading on a consent category (e.g. "User Experience" or "Personalization"), hook into Commanders Act's consent.update event:

Fire after consent is granted
javascript
1cact('on', 'consent.update', function (consent) {
2 // Adjust 'experience' to your CMP's consent category
3 if (consent && consent.experience === true) {
4 // Fire the Ekoo tag (container insertion + script)
5 cact('trigger', 'product_view', {
6 product_id: window.tc_vars && window.tc_vars.product_id,
7 });
8 }
9});

Performance — Load the Script Earlier

To shorten the time before the widget appears, you can create a second tag (All Pages trigger) that simply injects the Ekoo script into the <head>. See the Script loading page for the full pattern.

Common Pitfalls

  • Empty tc_vars — the tag fires before the dataLayer is populated. Force the order by using the page_view trigger (which waits for tc_vars) instead of DOM Ready.
  • Tag fires on every page — remember the tc_vars.page_type === 'product' filter or a URL filter to avoid loading the widget everywhere.
  • Script loaded twice — the querySelector('script[src*="widget-4.0.0-standalone"]') guard prevents adding the script multiple times.
  • SPA without a custom event — a regular page_view tag won't re-fire. Use cact('trigger', 'product_view', …) from your SPA code on each navigation.
  • CSP policy — if your site uses a strict CSP, add app.ekoo.co to the script-src directive.
  • Tag Cleaner enabled — can rewrite IIFEs and conditional logic. Uncheck the option if the code looks altered in unexpected ways.
ℹ️

Note

Need help? Check the FAQ or contact Ekoo support.

Commanders Act — Documentation — Ekoo