walkerOS for tracking specialists
Your GTM triggers work until someone changes the page
walkerOS becomes the collection layer under GTM, and tagging moves into your markup as data-elb attributes that change in the same pull request as the page. Marketing keeps adding tags in the GTM interface it already knows, and GTM receives every event as one destination.
Sounds familiar
Problems you have probably chased in GTM
data-elb attributes the tagging sits on the element itself, so it moves with the component and shows up in the same code review as the redesign. HTML attribute tagging shows the syntax.page_view either never fires or fires twice once a History Change trigger meets Enhanced Measurement.Your router calls elb('walker run') on each route change, and that call sends the page view. Set pageview: false on the GA4 destination and turn off history-based page views in Enhanced Measurement, so gtag doesn't send a second one. See virtual pageviews in SPAs.undefined. It works in preview and loses data on your fastest pages.With data-elb tagging, the event is built from the element's attributes at the moment the click, load or visible trigger fires, so it never waits on an earlier push. Calls made before walkerOS loads are queued and processed in order. The dataLayer source is the exception: it sees pushes in whatever order your site sends them.Migration path
Your existing dataLayer keeps working while you switch
You don't have to re-tag the site first. The dataLayer source wraps dataLayer.push: each push becomes a walkerOS event and then goes on to the original push, so GTM sees exactly what it saw before. You add data-elb attributes page by page, as your releases allow.
{
"sources": {
"dataLayer": {
"package": "@walkeros/web-source-datalayer",
"config": {
"settings": { "prefix": "dataLayer" },
"mapping": {
"dataLayer": {
"purchase": {
"name": "order complete",
"data": {
"map": {
"id": "transaction_id",
"total": "value",
"currency": "currency"
}
}
}
}
}
}
}
}
}Your existing purchase push becomes an order complete event. The push itself stays as it is, and so does every GTM tag listening for it.
Data quality
An event that fails its contract can stop before GA4
walkerOS can drop an event that breaks its contract before any destination, GA4 included, receives it. A developer writes the contract as JSON Schema in the flow config. In strict mode a failing event stops at the validate step. In pass mode it carries on with the verdict written onto it, and a later step can route on that.
{
"transformers": {
"validate": {
"package": "@walkeros/transformer-validate",
"config": {
"settings": { "contract": ["$contract.web"], "mode": "strict" }
},
"next": "ga4"
}
}
}In strict mode, an event that fails $contract.web stops here and never reaches ga4.
Consent
The gtag destination sends your Consent Mode signals
Every event carries a consent object, and each destination names the consent it needs in config.consent. Until that consent is granted, the destination's events wait in a queue. The gtag destination sends Consent Mode's denied consent default before GA4's config call and a consent update whenever consent changes, and como_advanced: true switches it to advanced mode.
elb('walker consent', { functional: true, marketing: false });The command a CMP source sends for you. Queued events then go out with the updated consent state.
Where it fits
What you keep, and what your GTM habits turn into
What stays
- GTM and the tags marketing adds there. If the container is already on the page, leave
loadScriptunset and the GTM destination pushes into its dataLayer without loading it again - Your GA4 property, its reports and DebugView
- Your existing
dataLayer.pushcalls, picked up by the dataLayer source - Your CMP and the banner visitors already see, connected through a CMP source or the
walker consentcommand
What changes
- Tags become destinations: GA4, Google Ads and GTM are settings on one gtag destination
- Triggers become attributes on the element, such as
data-elbaction="click:select" - Variables become mapping:
value: 'data.price'turns a product price into GA4'svalue - Preview mode becomes the CLI's
--simulateflag before deploy, and Observe once the flow runs - Publish becomes a config change with a diff and a reviewer
Start with the dataLayer source next to your current setup. Nothing you send today has to change.
What walkerOS does not do for you
- No template gallery. GTM has hundreds of vendor-maintained tag templates, while a walkerOS destination is a config entry someone writes and reviews.
- No point-and-click preview pane. You check your work with the CLI's
--simulateflag and Observe sessions. data-elbattributes live in the site's code. If you can't change the markup yourself, you start with the dataLayer source and need a developer for the attributes.- A developer writes the contract. walkerOS does not build one from your tracking plan.
- Config changes go through review, which is slower than clicking Publish in GTM.
Questions
Questions before you start
If we switch off GTM, don't we lose the point-and-click tagging that lets marketing add tags without a developer?
You don't have to switch it off. walkerOS becomes the collection layer and GTM one of its destinations, so marketing keeps adding tags in the interface it knows, fed by the same events. GTM stops carrying the collection logic: the custom HTML, hand-rolled consent checks and workarounds that pile up when the container is the only place data gets shaped. The GTM destination page shows how to feed a container that is already on the page.
GTM is free. What does walkerOS cost me to run? Don't I need servers now, and someone to look after them?
On the web, both are free: GTM costs nothing, and walkerOS is open source and runs in the browser. Server-side, both cost money, because a GTM server container also runs on infrastructure you rent. What differs is what the money buys: GTM is closed, so you pay for workarounds once the standard setup runs out, while walkerOS is open and your own developers can extend it. The cost section of the GTM comparison covers both.
GTM has a preview mode. How do I test what walkerOS collects before it goes live?
Run the CLI's push command with --simulate: it sends a real event through your exact flow config with the destinations mocked, before anything deploys. Because it is a command, it can run in CI on every change. Once the flow is live, an Observe session in the hosted app shows events as they arrive, and a self-hosted setup can stream the same records to its own endpoint through observers. The Observe page covers both.
Our tracking is DOM-based in GTM and mostly works. What actually changes when we switch, and what do we keep?
You keep your dataLayer.push calls, and you add data-elb attributes over time instead of switching in one go. What changes is how tracking breaks. A CSS-selector trigger depends on markup it doesn't own, so a template change can break it without anyone noticing, while an attribute on the element gets changed and reviewed together with that element. The dataLayer source lays out the migration in three phases.
Next steps