Google Tag Manager (GTM)
Overview
The @laioutr-app/gtm package integrates Google Tag Manager (GTM) into a Laioutr-powered Nuxt app. It does not register any orchestr handlers; instead it (1) emits an inline head bootstrap that sets the Google Consent Mode v2 denied default before gtm.js loads, (2) injects the GTM script and noscript iframe, (3) sends a Consent Mode update once the visitor answers the banner, and (4) registers an analytics destination that maps canonical events to GA4 names and pushes them to the data layer.
Configuration is minimal: containerId is required; layer (data layer variable name) and consentUpdates are optional. All options are in public runtime config. The module installs @laioutr-core/frontend-core on prepare so the consent store and analytics bus are available.
Configuration requirements
The module expects configuration under the key @laioutr-app/gtm in nuxt.config.ts (or via runtimeConfig). One option is required; one has a default.
Module options
| Option | Type | Description |
|---|---|---|
containerId | string | Record<string, string> | Google Tag Manager container ID (e.g. GTM-XXXXXX). A string installs one container for every market; a record maps each market id to its own container, and a market absent from the record gets no container at all. Used in the script URL https://www.googletagmanager.com/gtm.js?id={containerId}&l={layer} and in the noscript iframe. |
layer | string | Name of the data layer variable on window. Default: dataLayer. If you use a custom data layer name in GTM, set it here so the script and plugin use the same variable. |
consentUpdates | 'store' | 'external' | Who pushes gtag('consent', 'update', …). Default 'store' drives it from the Laioutr consent store. Set 'external' when your CMP emits Consent Mode itself, so the two do not race for the same signals — the inline denied default stays ours either way. |
Example configuration
// nuxt.config.ts
export default defineNuxtConfig({
modules: ['@laioutr-app/gtm'],
'@laioutr-app/gtm': {
containerId: process.env.GTM_CONTAINER_ID!,
layer: 'dataLayer',
},
});
Use an environment variable for containerId in production if you prefer not to hardcode it; the value is public so it can be exposed to the client.
Runtime behavior
- Head injection
The module adds to nuxt.options.app.head, in this order: (1) An inline bootstrap script that createswindow[layer], definesgtag, sets the Consent Mode v2 default, and pushes{ 'gtm.start': timestamp, event: 'gtm.js' }. (2) The asyncgtm.jsscript tag. (3) A noscript tag containing the GTM iframe (https://www.googletagmanager.com/ns.html?id={containerId}) for users with JavaScript disabled.
The bootstrap is inline and first deliberately: the default has to be registered before the Google tag initializes, or tags fire unconsented in the gap. It setsad_storage,ad_user_data,ad_personalization,analytics_storage,functionality_storageandpersonalization_storagetodenied;security_storagetogranted;wait_for_updateto500; plusads_data_redaction: trueandurl_passthrough: false. - Consent updates
With the defaultconsentUpdates: 'store', a client plugin watches the consent store and sends one completegtag('consent', 'update', …)carrying all six consent-gated signals — so a revocation is transmitted as faithfully as a grant.
The update is held until the visitor has actually answered. A CMP that activates on page load reporting its denied default does not trigger one: asserting a refusal nobody made would release the tagswait_for_updateis holding. A consent adapter that cannot report whether a decision was made still gets updates, since going silent would be worse. - Analytics destination
The plugin registers a destination declaringconsent: { purposes: ['analytics'] }. Canonical event types are mapped to GA4 names —ecommerce/add_to_cartbecomesadd_to_cart, while an app-namespacedacme/quote_requestedbecomesacme_quote_requested— money is converted from minor to major units per ISO 4217, and product category arrays are flattened toitem_category…item_category5. Nothing is pushed until theanalyticspurpose is granted. - Consent integration
For consent updates to apply, a consent adapter (e.g. @laioutr-app/cookiebot) must be registered and activated. If no consent adapter is active, the inline denied default stands and no tag storage is released.
Capabilities
This package does not provide orchestr queries, actions, links, or resolvers. It adds GTM to the page and connects it to the Laioutr consent store and analytics bus.
- GTM container – The GTM script and noscript iframe load your container. Configure tags, triggers, and variables in the GTM UI; the package only provides the data layer and consent updates.
- Consent Mode v2 – Every consent-gated signal defaults to
deniedinline in the head; when the visitor answers, the plugin sends one update reflecting the full state so Google tags (GA4, Ads) can respect it. - Analytics destination – Events emitted with
useAnalytics().track(Token, payload)are mapped to GA4 names and pushed to the data layer once theanalyticspurpose is granted. Use GTM triggers to react to those event names and parameters.
Backend requirements
- Google Tag Manager account – Create a GTM container and note the container ID (e.g.
GTM-XXXXXX). Configure your tags (e.g. GA4, conversion tracking) and triggers in the GTM UI. - @laioutr-core/frontend-core – The consent store and analytics bus come from frontend-core; ensure the app has this module. For consent-driven updates, also use a consent adapter (e.g. @laioutr-app/cookiebot).
Cookies and context
This package does not set or read cookies itself. GTM and the tags you configure in it (e.g. GA4) may set their own cookies; consent updates pushed by this package help those tags respect user consent when using Consent Mode.
Summary checklist
- Add @laioutr-app/gtm to Nuxt modules.
- Set containerId (and optionally layer and consentUpdates) under
@laioutr-app/gtm(e.g. from env). - Add a consent adapter (e.g. @laioutr-app/cookiebot) so Consent Mode updates are applied when the visitor accepts or revokes. Without one, everything stays denied.
- Emit events with
useAnalytics().track(Token, payload)in your app or in other apps; GTM receives them once theanalyticspurpose is granted. See Tracking. - Configure tags and triggers in the GTM container to react to data layer events and consent state.
Changelog
All changelogs are managed in CHANGELOG.md in the package’s GitHub repository. This app does not currently have a public repository under the Laioutr organization; when it is published there, use that repo’s CHANGELOG.md for release notes.
Emporix
Developer documentation for the Laioutr Emporix app package. Connect your Nuxt frontend to Emporix via the Emporix API with anonymous or authenticated auth.
Hygraph
Laioutr app package that connects a Nuxt storefront to Hygraph's Content API, Orchestr, Studio media, and Nuxt Image, using a sample content model as a starting point only.