Shopify merchants can use Matomo Tag Manager or the JavaScript tracking code with Shopify’s custom pixels in Customer Events. It is the supported method for implementing analytics and Ecommerce tracking.

This guide explains how to setup Matomo Tag Manager using a Shopify custom pixel.

Setup a Shopify custom pixel

With Shopify’s custom pixel, you modify and insert the Tag Manager container code directly in the store. This is an advanced implementation and requires knowledge of JavaScript and tag management concepts.

1. Add the custom pixel

  1. Go to your Shopify store and open Settings > Customer events to create and manage your pixels.
    view shopify customer events
  2. Click Add custom pixel and enter a Pixel name.
  3. Save by clicking Add pixel and you will automatically be directed to the pixel’s configuration page.
  4. You will need to define your customer privacy settings, add the modified container code, and subscribe to events.

You may see the warning: In a sandboxed environment, addEventListener may not behave as expected. This indicates that the custom pixel runs in an isolated execution context. It is expected and Matomo Tag Manager can still operate in this environment. However, avoid relying on triggers that depend on click listeners inside the sandbox and use data layer driven Custom Event triggers.

2. Prepare the Tag Manager container code

Shopify custom pixels run in a sandboxed environment, so the container code must be adapted for use inside the pixel.

  1. Open the Tag Manager container that will be used for Shopify tracking and in the left menu, click </> Install Code.
  2. Copy the container code and paste it to any text editor.
  3. Remove any surrounding HTML syntax (such as <script> tags) and include only the JavaScript portion of the tracking code.
  4. Modify your container code according to the example below and add the subscribed events.
  5. Click Save to update the custom pixel.
  6. In the Customer events page, expand the ellipsis menu (…) and select Connect to enable the pixel.

Code example

(function () {
  // Load Matomo Tag Manager container
  window._mtm = window._mtm || [];
  window._mtm.push({ "mtm.startTime": new Date().getTime(), event: "mtm.Start" });

  (function () {
    var d = document;
    var g = d.createElement("script");
    var s = d.getElementsByTagName("script")[0];
    g.async = true;
    g.src = "https://mysite.matomo.cloud/js/container_123.js";

    s.parentNode.insertBefore(g, s);
  })();

  function toNumber(value) {
    var n = Number(value);
    return Number.isFinite(n) ? n : 0;
  }

  function mapCheckoutLineItems(checkout) {
    var items = [];
    var lineItems = (checkout && checkout.lineItems) ? checkout.lineItems : [];

    for (var index = 0; index < lineItems.length; index++) {
      var li = lineItems[index];
      if (!li || !li.variant) continue;
// Note: Full-width brackets [index ] used to prevent rendering issues. Replace with square brackets [ ] before running the code.
      items.push({
        item_id: String(li.variant.id),
        item_name: String(li.title || ""),
        item_category: String((li.variant.product && li.variant.product.type) || ""),
        price: toNumber(li.variant.price && li.variant.price.amount),
        quantity: toNumber(li.quantity || 1)
      });
    }

    return items;
  }

  function mapCartLines(cart) {
    var items = [];
    var lines = (cart && cart.lines) ? cart.lines : [];

    for (var index = 0; index < lines.length; index++) {
      var line = lines[index];
      if (!line || !line.merchandise || !line.merchandise.product) continue;
// Note: Full-width brackets [index ] used to prevent rendering issues. Replace with square brackets [ ] before running the code.
      items.push({
        item_id: String(line.merchandise.product.id),
        item_name: String(line.merchandise.product.title || ""),
        item_category: String(line.merchandise.product.type || ""),
        price: toNumber(line.merchandise.price && line.merchandise.price.amount),
        quantity: toNumber(line.quantity || 1)
      });
    }

    return items;
  }
analytics.subscribe("all_standard_events", event => {
   console.log("Event data ", event?.data);
});
  // 1) Page views
  analytics.subscribe("page_viewed", function (event) {
    window._mtm.push({
      event: "page_viewed",
      page_url: event.context.window.location.href
    });
  });

  // 2) Product view
 analytics.subscribe("product_viewed", function (event) {
  window._mtm = window._mtm || [];
  window._mtm.push({
    event: "shopify.view_item",
    ecommerce: {
      items: [{
        item_id: String(event.data.productVariant.id),
        item_name: String(event.data.productVariant.title || ""),
        item_category: String((event.data.productVariant.product && event.data.productVariant.product.type) || ""),
        price: Number(event.data.productVariant.price && event.data.productVariant.price.amount) || 0,
        quantity: 1
      }]
    }
  });
});

  // 3) Collection view
  analytics.subscribe("collection_viewed", function (event) {
  window._mtm = window._mtm || [];
  window._mtm.push({
    event: "shopify.view_item_list",
    ecommerce: {
      item_list_name: String(event.data.collection && event.data.collection.title || "")
    }
  });
});

  // 4) Add to cart
  analytics.subscribe("product_added_to_cart", function (event) {
    window._mtm.push({
      event: "shopify.add_to_cart",
      ecommerce: {
        value: toNumber(
          event.data.cartLine &&
          event.data.cartLine.cost &&
          event.data.cartLine.cost.totalAmount &&
          event.data.cartLine.cost.totalAmount.amount
        ),
        items: [{
          item_id: String(event.data.cartLine.merchandise.product.id),
          item_name: String(event.data.cartLine.merchandise.product.title || ""),
          item_category: String(event.data.cartLine.merchandise.product.type || ""),
          price: toNumber(event.data.cartLine.merchandise.price && event.data.cartLine.merchandise.price.amount),
          quantity: toNumber(event.data.cartLine.quantity || 1)
        }]
      }
    });

  });

  // 5) Cart view
 analytics.subscribe("cart_viewed", function (event) {
  var cart = event && event.data && event.data.cart;
  if (!cart) return;

  window._mtm = window._mtm || [];

  var lines = cart.lines || [];
  var items = [];

  for (var i = 0; i < lines.length; i++) {
    var line = lines[i];
    if (!line || !line.merchandise || !line.merchandise.product) continue;

    items.push({
      item_id: String(line.merchandise.product.id),
      item_name: String(line.merchandise.product.title || ""),
      item_category: String(line.merchandise.product.type || ""),
      price: Number(line.merchandise.price && line.merchandise.price.amount) || 0,
      quantity: Number(line.quantity || 1) || 1
    });
  }

  window._mtm.push({
    event: "shopify.cart_updated",
    ecommerce: {
      value: Number(cart.cost && cart.cost.totalAmount && cart.cost.totalAmount.amount) || 0,
      items: items
    }
  });
});
  // 6) Purchase
  analytics.subscribe("checkout_completed", function (event) {
    var checkout = event.data.checkout;

    window._mtm.push({
      event: "shopify.purchase",
      ecommerce: {
        transaction_id: String(checkout.order && checkout.order.id || ""),
        value: toNumber(checkout.totalPrice && checkout.totalPrice.amount),
        subtotal: toNumber(checkout.subtotalPrice && checkout.subtotalPrice.amount),
        tax: toNumber(checkout.totalTax && checkout.totalTax.amount),
        shipping: toNumber(checkout.shippingLine && checkout.shippingLine.price && checkout.shippingLine.price.amount),
        discount: 0,
        currency: String(checkout.totalPrice && checkout.totalPrice.currencyCode || ""),
        items: mapCheckoutLineItems(checkout)
      }
    });
  });
})();

The code example initialises the Matomo Tag Manager data layer (window._mtm), loads your MTM container script, and then subscribes to Shopify Customer Events.

For each Ecommerce event, it builds a structured payload (including item IDs, names, categories, prices, quantities, and order/cart totals) and pushes it into the _mtm data layer. Tag Manager then uses these data layer events to trigger tags that send analytics and Ecommerce data to Matomo.

Configure Matomo Tag Manager

Next, configure Matomo Tag Manager to trigger tags based on the Shopify data layer events pushed by the custom pixel.

1. Create a Data Layer variable

  1. In Tag Manager, go to Variables.
  2. Click Create New Variable and select the Data Layer type.
  3. Provide a contextual name, for example, ‘Ecommerce’.
    create ecommerce data layer variable
  4. In the Data Layer variable name field, enter the actual variable name as referenced in the data layer, for example ecommerce.
  5. Click Create New Variable to save the changes.

2. Create Custom Event triggers

In this step, you will need to create custom event triggers to respond to each Shopify event pushed by the custom pixel. These triggers will fire tags when the corresponding Ecommerce interactions occur.

📄
The event names used in this guide match the examples provided in the custom pixel section. If you use different event names in your pixel implementation, ensure your custom event triggers and tags use the exact same names. Event names are case sensitive.
  1. Create six triggers – one for each Shopify event using the correct event names that the custom pixel pushes to the data layer.
  2. In Tag Manager, go to Triggers.
  3. Click Create New Trigger and select the Custom Event type.
  4. Provide a contextual name, for example, ‘Shopify – Add to cart’.
  5. Under Configure this trigger, enter the actual event name referenced in the custom pixel, for example shopify.add_to_cart.
  6. Click Create New Trigger to save the changes.
    create mtm shopify trigger
  7. Repeat these steps for every subscribed event:
    • shopify.page_viewed
    • shopify.product_viewed
    • shopify.collection_viewed
    • shopify.add_to_cart
    • shopify.cart_viewed
    • shopify.purchase

3. Add a Matomo tracking tag

If you do not have a Matomo pageview tracking tag, create one now to ensure all Ecommerce interactions are associated with a recorded visit.

  1. In Tag Manager, go to Tags.
  2. Click Create New Tag and select the Matomo Analytics type.
  3. Provide a contextual name that corresponds to the event trigger.
  4. Connect it to the default Matomo Configuration Variable and assign a pageview trigger.
  5. Click Create New Tag to save the changes.

Below are the exact Custom HTML tag contents for each event.

shopify.page_viewed

<script>
(function () {
  window._paq = window._paq || [];

  var dl = window._mtm || [];
  var last = dl.length ? dl[dl.length - 1] : null;
  if (!last || last.event !== "shopify.page_viewed") return;
})();
</script>

shopify.product_viewed

<script>
(function () {
  window._paq = window._paq || [];
  var ecommerce = {{Ecommerce}};
  if (!ecommerce || !ecommerce.items || !ecommerce.items.length) return;
  var item = ecommerce.items[0];
  _paq.push(['setEcommerceView',
    item.item_id,
    item.item_name || '',
    item.item_category || '',
    Number(item.price) || 0
  ]);
})();
</script>

shopify.collection_viewed

<script>
(function () {
  window._paq = window._paq || [];
  var dl = window._mtm;
  if (!dl || !dl.length) return;
  var last = dl[dl.length - 1];
  var ecommerce = last && last.ecommerce ? last.ecommerce : null;
  if (!ecommerce || !ecommerce.item_list_name) return;
  _paq.push(['setEcommerceView',
    false,
    false,
    ecommerce.item_list_name
  ]);
})();
</script>

shopify.add_to_cart

<script>
(function () {
  window._paq = window._paq || [];
  var ecommerce = {{Ecommerce}};
  if (!ecommerce || !ecommerce.items || !ecommerce.items.length) return;
  for (var j = 0; j < ecommerce.items.length; j++) {
    var item = ecommerce.items[j];
    _paq.push(['addEcommerceItem',
      item.item_id,
      item.item_name || '',
      item.item_category || '',
      Number(item.price) || 0,
      Number(item.quantity) || 1
    ]);
  }
  var value = Number(ecommerce.value) || 0;
  _paq.push(['trackEcommerceCartUpdate', value]);
})();
</script>

shopify.cart_viewed

analytics.subscribe("cart_viewed", function (event) {
  var cart = event && event.data && event.data.cart;
  if (!cart) return;

  window._mtm = window._mtm || [];

  var items = [];
  var lines = cart.lines || [];

  for (var i = 0; i < lines.length; i++) {
    var line = lines[i];
    if (!line || !line.merchandise || !line.merchandise.product) continue;

    items.push({
      item_id: String(line.merchandise.product.id),
      item_name: String(line.merchandise.product.title || ""),
      item_category: String(line.merchandise.product.type || ""),
      price: Number(line.merchandise.price && line.merchandise.price.amount) || 0,
      quantity: Number(line.quantity || 1) || 0
    });
  }

  window._mtm.push({
    event: "shopify.cart_updated",
    ecommerce: {
      value: Number(cart.cost && cart.cost.totalAmount && cart.cost.totalAmount.amount) || 0,
      items: items
    }
  });
});

shopify.purchase

<script>
(function () {
  window._paq = window._paq || [];

  var ecommerce = {{Ecommerce}};
  if (!ecommerce) return;

  // Add all purchased items
  if (ecommerce.items && ecommerce.items.length) {
    for (var i = 0; i < ecommerce.items.length; i++) {
      var item = ecommerce.items[i];

      _paq.push(['addEcommerceItem',
        item.item_id,
        item.item_name || '',
        item.item_category || '',
        Number(item.price) || 0,
        Number(item.quantity) || 1
      ]);
    }
  }

  // Track the order
  _paq.push(['trackEcommerceOrder',
    ecommerce.transaction_id,
    Number(ecommerce.value) || 0,
    Number(ecommerce.subtotal) || 0,
    Number(ecommerce.tax) || 0,
    Number(ecommerce.shipping) || 0,
    Number(ecommerce.discount) || 0
  ]);
})();
</script>

To track cart updates correctly, Matomo requires a full cart snapshot each time the cart is updated. Matomo does not support tracking individual cart item changes in isolation.

After a customer adds or removes an item, you must send:

  • An addEcommerceItem call for every item currently in the cart, including items added previously.
  • A final trackEcommerceCartUpdate call containing the total cart value.

In the example setup used in this guide, the cart snapshot is sent when the customer views the /cart page. At that point, all items and the current cart total are pushed to Matomo.

Test and publish the container

  1. Open your storefront and use the browser Developer Tools.
  2. In the Console tab, check that the Tag Manager container loads without errors.
  3. Browse your store and trigger actions such as viewing a product or adding to cart and confirm events appear in the Console.
  4. In the Network tab, filter for: matomo.php and perform the following tests:
    • View a product and confirm the request contains product data.
    • View the cart page and confirm the request contains idgoal=0 and ec_items.
    • Complete a purchase and confirm the request has ec_id and revenue.
  5. The presence of ec_items and idgoal=0 confirms Ecommerce tracking is working.
  6. View the Visitor > Real-time and Visit Log report that shows the recorded actions.
    matomo visit log
  7. After successfully testing, publish the container.

Troubleshooting

If tracking does not work as expected, validate the setup step by step.

  1. Verify the connection to Matomo Tag Manager by viewing the Network tab in the browser’s Dev Tools.
  2. Check the Shopify custom pixel executes without JavaScript errors.
  3. Identify if sandbox-related errors block execution.
  4. Ensure the MTM container file loads successfully (HTTP 200, not 404). In the Network tab, filter by container_ and reload the page to confirm the container file loads with status 200.
  5. If the event exists in _mtm but data is not sent to Matomo, verify the custom event trigger name matches exactly (case sensitive).
  6. Confirm the correct triggers are attached to the tag.
  7. Review the setup steps for the custom pixel and Matomo Tag Manager configuration.

Next steps

Once tracking is confirmed, use the Ecommerce reports to analyse product performance, completed product views, and conversion rate to evaluate sales performance. You can also extend the implementation by creating Custom Reports and Segments to report on specific product categories, SKUs, or customer groups in more detail.

Previous FAQ: How to track 404 error pages in Matomo Tag Manager