Analytics
Google Tag
Configure the Google tag
Copy the tag ID, for example G-XXXXXXXXXX for Google Analytics or
AW-XXXXXXXXX for Google Ads. Remove any gtag.js snippet already in your
HTML and any tag-manager entry that loads the same tag.
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 | Tag ID passed to gtag('config', ...) and the loader URL. The helper trims it. Empty or whitespace-only values log an error and the script does not load. |
category | Required | measurement for Analytics, marketing for Ads and Floodlight. With loadMode: 'after-consent', gtag/js waits for it. With 'always', it sets the script's permission, which callbacks receive, but does not delay loading. |
loadMode | 'always' | When gtag/js loads. See choose when Google loads. |
config | None | Parameters passed as the third argument to gtag('config', id, config). |
consentMapping | The table below | Replaces the category-to-Google mapping. |
The deprecated script option overrides fields of the returned script. Use the
options above instead.
Choose when Google loads
loadMode | Until the category is allowed | After the category is allowed |
|---|---|---|
'always' | Loads gtag/js and sends consent default with the current permissions, then config. | Sends consent update with the mapped types granted. |
'after-consent' | Sends nothing to Google. The helper creates no dataLayer or gtag function. | Loads gtag/js once. consent default carries the current permissions and comes before config. Later changes send 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 gtag/js loads on the first page unless a saved refusal or a
privacy signal restricts the category; see
policies. necessary is always allowed, so
category: 'necessary' loads gtag/js before a choice in either mode. Use
measurement or marketing.
This 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.
When the visitor withdraws the category, c15t reloads the page and the new
page does not load gtag/js. With reloadOnConsentRevoked: false, the tag
already running stays on the page and gets an update that denies the
withdrawn types. A later grant reuses it instead of loading a second copy.
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.
Verify the Google tag
On a plain HTML page with the script tag, you gate Google's snippet instead and it loads only after consent; see HTML scripts.
With the default loadMode: 'always':
- In a private window with an opt-in policy, load the page.
gtag/jsloads. In Google Tag Assistant, theconsentdefaultcommand comes beforeconfig, withanalytics_storageandad_storageset todenied. - Open Privacy settings and allow the tag's category. Tag Assistant shows an
updatecommand that grants the mapped types. - Turn the category off again and save. c15t reloads the page, and the new page starts with those types denied.
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 the tag's category.
gtag/jsloads once. In Tag Assistant, the first command isconsentdefaultwith the mapped types granted, beforeconfig. - Turn the category off again and save. c15t reloads the page, and the new page makes no request to Google.
In either mode, with client-side navigation, check that each route change
sends one page view. A separate router integration that also sends
page_view doubles the count.
See the consent verification guide for hosting checks.