Tag managers
Google Tag Manager
Configure Google Tag Manager
Copy the container ID, which starts with GTM-, from your GTM workspace.
Remove the existing GTM snippet, its <noscript> iframe and any framework
plugin that loads the same container.
Register the scripts
Complete your framework quickstart first. Keep its Inth endpoint, policy, styles and consent UI. Remove the vendor's original script, SDK initializer or tag-manager entry, so the vendor loads only through c15t.
The vendor pages put the helper in src/consent-scripts.ts. If your framework
quickstart already has a scripts array, such as the one in c15t.config.ts
in the Next.js guide, add the helper to that array instead of creating a
second file.
The scripts export is a configuration, not an initializer. Add it to the c15t provider you already have, at the registration
point for your framework below. These are edits to that provider, not a second
provider.
Add the configuration to scripts in c15t.config.ts, next to
next.config.ts:
Keep the rest of your config, such as mode and routePrefix, in the
same call. ConsentRoot reads the config in the browser, so the layout
keeps passing only state. App Router, Pages Router and static export all
read the same file. See
Next.js scripts and embeds.
Options
| Option | Default | Behavior |
|---|---|---|
id | Required | Container ID, for example GTM-1234XXX. The helper trims it. Empty or whitespace-only values log an error and the script does not load. |
dataLayer | 'dataLayer' | Queue name. A custom name adds &l=<name> to the container URL and renames the helper's queue function to <name>Gtag. |
updateEventName | 'consent-update' | dataLayer event pushed after every consent update. Use it as a GTM trigger. |
consentMapping | The table below | Replaces the category-to-Google mapping. |
loadMode | 'always' | When gtm.js loads. See choose when the container loads. |
category | 'necessary', or { or: ['measurement', 'marketing'] } with loadMode: 'after-consent' | Consent condition for the container. With 'after-consent', gtm.js waits for it. With 'always', it sets the script's permission, which callbacks receive, but does not delay loading. |
Choose when the container loads
loadMode | Until the category is allowed | After the category is allowed |
|---|---|---|
'always' | Loads gtm.js and sends consent default with the current permissions before the gtm.js start event. | Sends consent update, then the consent-update event. |
'after-consent' | Sends nothing to Google. The helper creates no dataLayer or gtag function. | Loads gtm.js once. consent default carries the current permissions and comes before the gtm.js start event. Later changes send update and consent-update. |
Use 'after-consent' when your policy forbids any request to Google before
the visitor opts in:
'after-consent' waits for the category to be allowed, not for a recorded
choice. Under an opt-in policy, that happens when the visitor allows it.
Under an opt-out or none policy, optional categories are allowed before a
choice, so gtm.js loads on the first page unless a saved refusal or a
privacy signal restricts the category; see
policies.
necessary is always allowed, so a gated container needs another category.
This mode defaults to { or: ['measurement', 'marketing'] }. A container
usually holds Analytics tags and advertising tags, and this default loads it
once the visitor allows either purpose. Google tags inside still follow the
Consent Mode types, so an Ads tag stays restricted for a visitor who allowed
only measurement. If the container holds only Analytics tags, pass
category: 'measurement'.
This mode gives up part of Consent Mode. With 'always', Google tags send
cookieless pings while a type is denied, and Google uses them to model
conversions and behavior for visitors who refused or have not chosen. With
'after-consent', visitors whose category is denied send nothing, so Google
has no data to model them from. Reports cover only visitors who allowed the
category.
The container starts with the visitor's current choice in its default
command. On the page where the visitor accepts, it loads after the choice, so
no consent-update event fires for that choice. Triggers that wait only for
consent-update first fire on a later change. Use the tag's consent settings,
described below, for tags that should fire on the first page.
When the visitor withdraws the category, c15t reloads the page and the new
page does not load gtm.js. With reloadOnConsentRevoked: false, the running
container stays on the page and gets an update that denies the withdrawn
types. A later grant reuses it instead of starting a second container.
Google loads before a choice by default
With the default loadMode: 'always', the googleTagManager and gtag
helpers set alwaysLoad: true. Before the visitor chooses, the helper creates
the dataLayer queue, sends gtag('consent', 'default', ...) with the current
permissions and loads Google's script. After each permission change it sends
gtag('consent', 'update', ...). Google's tags then adjust what they store and
send; see Google's
Consent Mode overview.
So the browser contacts Google before consent. If your policy requires no
Google request until the visitor allows it, set loadMode: 'after-consent'.
The helper then creates nothing and requests nothing until its category is
allowed. When it loads, it sends the default command first, with the
permissions at that moment, and update commands after later changes. Under
an opt-out or none policy, optional categories are allowed before a
choice, so the helper loads on the first page.
How categories map to Google consent types
| c15t category | Google consent types |
|---|---|
necessary | security_storage |
functionality | functionality_storage |
measurement | analytics_storage |
marketing | ad_storage, ad_user_data, ad_personalization |
experience | personalization_storage |
Each Google type is granted when its category is allowed and denied
otherwise. If a visitor turns off the helper's vendor, every optional type is
sent as denied. The consentMapping option replaces the whole table, so
include every category you still want signalled.
Under an opt-out policy, the default command can grant types before the visitor has chosen anything. That is a permission, not a recorded choice.
Configure consent inside the container
With the default loadMode: 'always', googleTagManager uses the necessary
category and alwaysLoad, so the container loads on every page. With
'after-consent', it loads once its category is allowed. In both modes,
consent signals reach Google tags that support Consent Mode. Other tags in the
container, such as a third-party pixel added as Custom HTML, ignore those
signals and fire on their triggers.
For each non-Google tag, add a consent requirement in the tag's consent
settings, or fire it from a Custom Event trigger on consent-update. If a
vendor must make no request before consent, remove it from the container and
register its own c15t helper instead.
Measure opt-in rate
If you run a banner experiment, the backend already counts visitors and
choices per arm. To see the arm in GTM as well, forward the onSurfaceShown and
onChoiceRecorded callbacks, which carry experiment: { id, arm }. See
banner experiments.
Verify Google Tag Manager
With the default loadMode: 'always':
- In a private window with an opt-in policy, load the page.
gtm.jsloads. In Google Tag Assistant, the first consent command isdefaultwithanalytics_storageandad_storageset todenied. - Click Reject, then reload. The
defaultcommand still denies every optional type, and in DevTools Network no non-Google tag in the container fires. - Open Privacy settings and allow measurement. Tag Assistant shows an
updatecommand withanalytics_storageset togranted, followed by aconsent-updateevent, and measurement tags fire. - Turn measurement off again and save. c15t reloads the page, and the new
page starts with
analytics_storagedenied.
With loadMode: 'after-consent':
- In a private window with an opt-in policy, filter DevTools Network by
googleand load the page. No request appears, andwindow.dataLayerisundefinedin the Console. - Allow measurement.
gtm.jsloads once. In Tag Assistant, the first command isconsentdefaultwithanalytics_storageset togranted, before the container starts. - Allow marketing as well. Tag Assistant shows an
updatecommand that grants the ad types, followed by aconsent-updateevent. No secondgtm.jsrequest appears. - Turn both off again and save. c15t reloads the page, and the new page makes no request to Google.
See the consent verification guide for navigation and hosting checks.