Skip to content
Twilight React Playground
ثيم رائدaren

useGtm

hookAdvancedserverbrowserlive demo

Pushes ecommerce events (view, impressions, click, add to cart, checkout, purchase) to Google Tag Manager's dataLayer when the store has GTM.

import { useGtm, UseGtmResult } from '@salla.sa/twilight-theme-engine/hooks/useGtm';

In plain words

If the merchant connected Google Tag Manager, you can report what shoppers do: detail(product) when they view a product, add(product) when they add it to the cart, and so on. The hook builds the event objects in the shape GTM ecommerce tags expect.

If the store has no GTM container, enabled is false and every call quietly does nothing.

Signature

function useGtm(): UseGtmResult

interface UseGtmResult {
  enabled: boolean;
  push: (data: Record<string, unknown>) => void;
  viewPage: (pageType: string) => void;
  detail: (product: Product) => void;
  impressions: (products: Product[], list?: string) => void;
  click: (product: Product, list?: string, position?: number) => void;
  add: (product: Product, quantity?: number) => void;   // quantity 1
  remove: (item: CartItem) => void;
  checkout: (cart: Cart, step?: number) => void;        // step 1
  purchase: (orderId: string | number, cart: Cart) => void;
}

Try it live

useGtm() is enabled when the store has a Google Tag Manager container (this demo store does), and then pushes real events to this page's dataLayer.Try this: press the button, then read the last dataLayer entry: the object the hook just pushed. With enabled false, nothing would be pushed.
Real requests to the demo store
Storefront canvas · ar · RTL

enabled: true · store.settings.keys.gtm: ["GTM-TGFC6FV"]

Controls
What a theme writes
import { useEffect } from 'react';
import { useGtm } from '@salla.sa/twilight-theme-engine/hooks/useGtm';

export function TrackPageView() {
  const { viewPage } = useGtm();
  useEffect(() => {
    viewPage('playground'); // does nothing when the store has no GTM container
  }, [viewPage]);
  return null;
}

Example

app/components/product/TrackProductView.tsx
import { useEffect } from 'react';
import { useGtm } from '@salla.sa/twilight-theme-engine/hooks/useGtm';
import type { Product } from '@salla.sa/twilight-theme-engine/types';

export function TrackProductView({ product }: { product: Product }) {
  const { detail } = useGtm();

  useEffect(() => {
    detail(product); // an effect: it writes to window.dataLayer
  }, [detail, product]);

  return null;
}

How it behaves

  • enabled is store.settings.keys.gtm having at least one id. push returns early when it is false; otherwise window.dataLayer = window.dataLayer || [] and window.dataLayer.push(data).

  • Events are { event, ecommerce: { currencyCode, event_id, <detail | impressions | click | add | remove | checkout | purchase>: … } }, with event_id like detail_<timestamp>_<random>. viewPage pushes { event: 'viewPage', pageType }.

  • Products become { id, name, price: sale_price, brand: brand?.name, category: category?.name, quantity: 1 }, plus position and list for impressions and clicks.

  • currencyCode is the product's currency for product events; otherwise the logged-in customer's preferences.currency_code, else SAR. remove, checkout and purchase always use that fallback.

  • The functions are memoised on enabled and the currency, so they are safe as effect dependencies.

  • It calls useUser() for the currency. The GTM container itself is loaded by the default body:start handler; this hook only pushes events.

Gotchas

  • Product events send price: product.sale_price, and the Store API answers sale_price: null for some products that are not on sale (checked on the demo store). Push your own event with push() if your tags need a price every time.

  • Call the functions from effects or event handlers, never while rendering: they write to window, which the server does not have, and a render can run more than once.

  • packages/theme-engine/docs/useLocation.md passes location.pathname to viewPage, whose parameter is a page type, not a path.

Related

Source and docs