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

SeoCartWidget

componentAdvancedbrowserlive demo

Draws nothing: on the cart page it pushes Google Tag Manager checkout events for each step and sets the browser tab title.

import { SeoCartWidget, SeoCartWidgetProps } from '@salla.sa/twilight-theme-engine/components/cart';

In plain words

Some components exist only for what they do, not for what they show. SeoCartWidget renders nothing. Put it on the cart page with the cart and it tells Google Tag Manager "a checkout started" (step 1), reports the shipping and payment steps as the shopper moves through checkout, and sets the tab title to something like "Cart — Store name".

It is lazy, so it goes inside <Suspense>.

Signature

const SeoCartWidget: React.LazyExoticComponent<(props: SeoCartWidgetProps) => null>

interface SeoCartWidgetProps {
  cart: Cart;
  step?: 1 | 2 | 3;   // 1 = cart, 2 = shipping, 3 = payment
}

Try it live

Mount SeoCartWidget with your guest cart: it pushes a GTM checkout event to this page's dataLayer and renames the browser tab.Try this: tick mounted, read the values, then change the step and read again: each step pushes a new event and a new title.
Real requests to the demo store
Storefront canvas · ar · RTL
Runs in the browser…
Controls
mountedNothing is pushed until the widget mounts.
What a theme writes
import { Suspense } from 'react';
import { SeoCartWidget } from '@salla.sa/twilight-theme-engine/components/cart';
import type { Cart } from '@salla.sa/twilight-theme-engine/types';

// Only on the cart page: it renames the browser tab when it mounts.
export function CartAnalytics({ cart }: { cart: Cart }) {
  return (
    <Suspense fallback={null}>
      <SeoCartWidget cart={cart} />
    </Suspense>
  );
}

Example

app/routes/cart.tsx (excerpt)
import { Suspense } from 'react';
import { SeoCartWidget } from '@salla.sa/twilight-theme-engine/components/cart';
import type { Cart } from '@salla.sa/twilight-theme-engine/types';

export function CartAnalytics({ cart }: { cart: Cart }) {
  return (
    <Suspense fallback={null}>
      <SeoCartWidget cart={cart} />
    </Suspense>
  );
}

How it behaves

  • On mount, and when cart, step or the currency changes, it pushes { event: 'checkout', ecommerce: { checkout: { actionField: { step }, products, cart_total, currency } } } to window.dataLayer, creating the array if needed. products lists only lines with is_available, as { id: product_id, name, price, quantity }.

  • It subscribes to the SDK's checkout::step.shipping and checkout::step.payment events and pushes steps 2 and 3 when they fire.

  • The title is the translation common.titles.cart, .shipping or .checkout (the key itself when missing), then " — " and the store name. The currency is useTwilight().currency.code, else SAR.

  • It never loads GTM. The store's container is loaded by the default body:start handler (GoogleTagBody) when the store settings name one.

Gotchas

  • The push reruns whenever the cart object is a different object. Passing a cart rebuilt on every render ({ ...cart }) pushes a new checkout event on every render.

  • It sets document.title on mount and never restores it. Mounted anywhere but the cart page it renames that page too.

Related

Source and docs