@stackonward/google-tag-manager
v0.3.0
Published
Google Tag Manager analytics transport and declarative container generator
Maintainers
Readme
@stackonward/google-tag-manager
Google Tag Manager transport and declarative container generator for the
provider-neutral @stackonward/analytics-core contract.
Use this package when an application needs to project validated analytics envelopes into the GTM data layer, or generate a reviewable GTM web-container import. It does not load the GTM script or mutate a Google account.
Install
pnpm add @stackonward/google-tag-manager @stackonward/analytics-coreRuntime transport
import { createAnalyticsClient } from "@stackonward/analytics-core";
import { createGTMTransport, type GTMMessage } from "@stackonward/google-tag-manager";
declare global {
interface Window {
dataLayer: GTMMessage[];
}
}
const analytics = createAnalyticsClient();
window.dataLayer ??= [];
analytics.registerTransport(
createGTMTransport({
push: (message) => window.dataLayer.push(message),
}),
);
await analytics.track({
name: "search",
properties: { search_term: "deployment checklist" },
});Every event clears the provider-owned stackonward namespace and stale
ecommerce state before dispatch, then clears both again after GTM processes
the event. Recommended ecommerce events keep their GA4 names and move
ecommerce fields into the GA4 ecommerce object.
projectGTMEvent is available when a caller needs to inspect projection results
without registering a transport. Invalid Google names, reserved data-layer
fields, context/property collisions, parameter limits, and malformed ecommerce
items return typed validation issues. createGTMTransport converts those issues
into GTMProjectionError and does not push a partial event.
Recommended event types
The optional event entry provides typed GA4 event and item contracts:
import type { GA4Event, GA4Item } from "@stackonward/google-tag-manager/events";
const item: GA4Item = { item_id: "sku_123", item_name: "Starter plan", price: 19 };
const event: GA4Event = {
name: "purchase",
properties: {
transaction_id: "order_123",
currency: "USD",
value: 19,
items: [item],
},
};Container generation
import { createGTMContainer } from "@stackonward/google-tag-manager/container";
const container = createGTMContainer({
schemaVersion: 1,
provider: "google-tag-manager",
applicationId: "product-a",
measurementId: "G-ABC1234567",
customParameters: ["plan"],
});The immutable result uses GTM export format version 2 and contains:
- one Google tag on
Initialization - All Pageswith automatic page views disabled; - one dynamic GA4 event tag for non-commerce events;
- one dynamic GA4 ecommerce event tag that reads ecommerce data from the data layer;
- separate custom-event triggers restricted to GA-compatible event names and StackOnward
event_sourcevalues; - GTM built-in variables for Page URL, Page Hostname, Page Path, Referrer, and Event;
- data-layer variables for the built-in and configured custom parameters inside the provider namespace.
Import the JSON into a GTM web-container workspace, preview it, and publish only
after review. The generated GTM-XXXXXXX container ID is an import sentinel;
the destination workspace owns its real container ID.
parseGTMContainerConfig, gtmContainerConfigSchema, and
getGTMContainerConfigJsonSchema are exported from the /container entry for
configuration validation and tooling.
Entry points
| Entry point | Purpose |
| ------------------------------------------- | ------------------------------------------------------------ |
| @stackonward/google-tag-manager | Event projection, transport, messages, and projection errors |
| @stackonward/google-tag-manager/events | Typed GA4 recommended-event and item contracts |
| @stackonward/google-tag-manager/container | Config schema, parser, JSON Schema, and container generator |
Lifecycle and boundaries
beforeSendcan wait for a caller-owned script or consent lifecycle before a push.disposereleases caller-owned transport resources; later sends fail explicitly.- Event names and custom parameters must satisfy Google's naming and reserved-prefix rules.
- Account OAuth, workspace selection, import, preview, and publication remain operator-owned steps.
Compatibility
- Node.js 20 or newer
- Modern browsers for
window.dataLayertransport use - ESM with TypeScript declarations
Related packages
@stackonward/analytics-coreowns provider-neutral validation and delivery.@stackonward/google-tag-manager-nuxtloads the script and composes consent-aware Nuxt lifecycle behavior.@stackonward/cligenerates the same container artifact from a checked configuration file.
