Webclat / Tealium Practice

How do I call utag.link/utag.view correctly for content that loads dynamically in a Tealium-tagged SPA?

Answer

Use utag.view() for anything that represents a new "page" the visitor has navigated to - including a client-side route change with no full page load - and utag.link() for an interaction on top of an already-tracked view: a click, a modal open, an add-to-cart. Call both only after confirming utag and the specific method exist, and always pass a fresh data object built from the current UDO state at call time. Reusing a stale object captured earlier is the single most common cause of dynamically loaded content reporting the wrong page or values.

Why this happens

SPA frameworks don't trigger a browser-level page load on route change, so Tealium - built around discrete page loads by default - never sees the new "page" unless you tell it. Two separate problems get conflated under "SPA tracking is broken":

  • Nothing fires at all, because there's no page-load event to hook a view call to.
  • Something fires, but with data captured at component-mount time rather than at the actual moment of the view or click - so values are stale for anything that resolves after the component renders: async-resolved IDs, personalization content, lazy-loaded product data.

Fix it

  1. Pick one router lifecycle hook - the "route change complete" event, not "route change started" - as the single place utag.view() is called for navigation. Don't scatter view calls across multiple components.
  2. Before each utag.view() call, rebuild the data object from current state. Don't hoist an object built during the component's initial render if the underlying data can still change before this line executes.
  3. Guard every call: if (window.utag && typeof utag.view === "function") { utag.view({...}); }. This matters more in SPAs than static pages, because the route can change before the library finishes loading on the very first navigation.
  4. For content that mounts after the view already fired - a modal, an infinite-scroll batch, a lazy widget - use utag.link() from that content's own mount/interaction handler, not the router hook. Treat it as an interaction on the existing view, not a new view.
  5. If the same route can re-render without the URL changing (a tab switch inside one route), decide explicitly whether that's a new utag.view() or a utag.link(), and apply that rule consistently - Tealium has no framework-aware way to infer this for you.

How to verify it worked

  • Open Tealium's Trace/Live tool, navigate through several client-side routes without a full reload, and confirm exactly one trace event fires per route change - not zero, not two.
  • Inspect utag.data (or utag_data on older syntax) in the console immediately after a view fires, and compare it field-by-field to what's actually rendered on screen. A mismatch there is the stale-object symptom, not a firing-timing symptom.
  • Watch the Network tab for the outbound beacon requests fired by the vendor tags configured on that view. Trace shows what Tealium thinks it sent; the Network tab confirms whether the downstream tag actually sent a beacon.
  • For the modal/lazy-content case, trigger the interaction and confirm exactly one utag.link() call appears in trace, correlated to the click - not to the page load that happened to precede it.

Get your SPA route changes tracked reliably.

We map every route and dynamic interaction to the correct utag.view/utag.link call, then trace it live to prove it.

Audit My SPA Tracking