Skip to main content

Integrate JOIN with GTM

Integrate your JOIN video widgets using Google Tag Manager.

⚠️ Warning: Integrating widgets through GTM can cause CLS (Cumulative Layout Shift) on your website. This is not specific to JOIN Stories but applies to any integration injected into the DOM through a Tag Manager.

Prerequisites


Before you begin, retrieve the following information from your JOIN back office:

1. Domain name
Your domain in the following format: your-brand.my.join-stories.com
​

2. Widget alias
​The identifier of your widget (e.g. homepage, products, pdp)
​

Widget type


​JOIN offers two widget types. Choose the one that matches your use case :

  • Standard Widget : Displays the same content for all visitors.

    Use cases: Homepage, category pages, landing pages.

  • Product Page Widget : Displays content specific to the product being viewed.

    Use case: Product pages (PDP).


Integration with Google Tag Manager

With GTM, you do not create the container in HTML. Instead, the container is created dynamically in JavaScript and inserted into a specific location on your page using a CSS selector.

In GTM, create a Custom HTML Tag and paste the snippet corresponding to your widget type.
​
Standard Widget

<script>
(function () {
// ─── Configuration ───────────────────────────────────────────
var domainName = "votre-marque.my.join-stories.com"; // :arrow_left: Votre domaine JOIN
var alias = "votre-alias"; // :arrow_left: Alias du widget
var divId = "join-widget";
var targetSelector = ".product-info"; // :arrow_left: SΓ©lecteur CSS de l'Γ©lΓ©ment cible
// ─────────────────────────────────────────────────────────────

var target = document.querySelector(targetSelector);
if (!target) {
console.warn("JOIN: Γ‰lΓ©ment cible non trouvΓ© (" + targetSelector + ")");
return;
}

var container = document.createElement("div");
container.id = divId;

// MΓ©thodes d'insertion disponibles (gardez-en une seule) :
target.insertAdjacentElement("afterend", container); // Après l'élément
// target.insertAdjacentElement("beforebegin", container); // Avant l'Γ©lΓ©ment
// target.appendChild(container); // Γ€ la fin de l'Γ©lΓ©ment
// target.prepend(container); // Au dΓ©but de l'Γ©lΓ©ment

var script = document.createElement("script");
script.src = "https://" + domainName + "/widgets/" + alias + "/index.js";
script.setAttribute("data-join-widget-id", divId);
script.setAttribute("data-join-widget-alias", alias);
script.type = "text/javascript";

container.appendChild(script);
})();
</script>
  • Product Page Widget

    This snippet loads the widget specific to the product currently being viewed, with an automatic fallback to the default widget if no content is associated with the product.

<script>
(function () {
// ─── Configuration ───────────────────────────────────────────
var domainName = "votre-marque.my.join-stories.com"; // ⬅️ Votre domaine JOIN
var alias = "pdp"; // ⬅️ Alias du widget PDP
var divId = "join-widget-pdp";
var targetSelector = ".product-form__quantity"; // ⬅️ SΓ©lecteur CSS de l'Γ©lΓ©ment cible
var productId = {{Page.ProductId}}; // ⬅️ Variable GTM contenant l'ID produit
// ─────────────────────────────────────────────────────────────

var target = document.querySelector(targetSelector);
if (!target) {
console.warn("JOIN: Γ‰lΓ©ment cible non trouvΓ© (" + targetSelector + ")");
return;
}

var container = document.createElement("div");
container.id = divId;

target.insertAdjacentElement("afterend", container);

var baseUrl = "https://" + domainName + "/widgets/" + alias;

var script = document.createElement("script");
script.src = baseUrl + "/" + productId + ".js";
script.setAttribute("data-join-widget-id", divId);
script.setAttribute("data-join-widget-alias", alias);
script.type = "text/javascript";

script.onerror = function () {
script.remove();
var fallback = document.createElement("script");
fallback.src = baseUrl + "/index.js";
fallback.setAttribute("data-join-widget-id", divId);
fallback.setAttribute("data-join-widget-alias", alias);
fallback.type = "text/javascript";
container.appendChild(fallback);
};

container.appendChild(script);
})();
</script>

Important points

CSS selector (targetSelector)

Choose a stable element that is always present on the page.

Common examples:

  • .product-info β€” Product information block

  • .product-form__quantity β€” Quantity selector

  • .product-description β€” Product description

  • #add-to-cart-button β€” Add-to-cart button

Recommended GTM trigger:
Use DOM Ready or Window Loaded to ensure the target element exists before the script runs.
​

Insertion method

Only one insertion method should be active at a time (the others should remain commented out).

By default:

insertAdjacentElement("afterend", container)

inserts the widget immediately after the target element.


Reduce CLS (optional but recommended)

To minimize layout shifts, add a minimum height to the widget container using a separate GTM HTML tag or directly in your theme.

<style>
#join-widget,
#join-widget-pdp {
display: block;
width: 100%;
min-height: 114px;
margin: 20px 0;
}

/* Hauteur adaptΓ©e sur Γ©crans larges */
@media (min-aspect-ratio: 3/4) {
#join-widget,
#join-widget-pdp {
min-height: 283px;
}
}
</style>


​
Audience data (optional)

You can send audience segmentation data to JOIN before the widget loads by pushing it to window.jDataLayer.

Audience (custom segmentation)

<script>
window.jDataLayer = window.jDataLayer || [];
window.jDataLayer.push({
type: "audience",
data: "premium" // ex: "premium", "member", "vip"
});
</script>

These scripts must be executed before the JOIN widget loading tags in GTM.

Did this answer your question?