useXAFConsentTracking
useXAFConsentTracking
The script-injection engine behind <XAFCookieConsent />. Reads app.config.xAffiliate.tracking and dynamically injects GTM / GA4 / Clarity / Meta Pixel / Hotjar / Segment / LinkedIn Insight / etc. only after the matching consent category is granted.
The composable is auto-imported in Nuxt and listens for consent:updated window events fired by the banner. It's also bootstrapped by the layer's xaf-consent-tracking.client.ts plugin, which calls rehydrate() on client mount so re-visitors get their tracking scripts loaded immediately — no flash of banner.
useConsentTracking in the minor after v0.6.0 — the old name collided with nuxt-x-marketing's identically-named composable when a site extends both layers.Most sites don't need to call this composable directly; the banner component does it for you. Reach for it when you want to programmatically grant consent or read the current state from elsewhere in your app.
Usage
const consent = useXAFConsentTracking()
// Programmatic consent
consent.acceptAll()
consent.rejectAll()
consent.setConsent({
necessary: true,
analytics: true,
marketing: false,
preferences: true,
})
// Read state
consent.hasConsent('analytics') // boolean
consent.state.value // reactive { necessary, analytics, marketing, preferences }
consent.hasDecision.value // true once the visitor has saved any decision
// Manual hydration (called by the client plugin on mount)
consent.rehydrate()
consent.init() // rehydrate + register consent:updated listener
Returns
| Key | Type | Description |
|---|---|---|
state | Ref<{ necessary, analytics, marketing, preferences }> | Reactive consent state. necessary is always true. |
hasDecision | Ref<boolean> | true once the visitor has saved any decision (i.e. localStorage has the key). |
setConsent(state) | (ConsentState) => void | Apply a full consent map, write to localStorage, fire scripts, and broadcast consent:updated. |
acceptAll() | () => ConsentState | Grant every category. Returns the new state. |
rejectAll() | () => ConsentState | Grant only necessary. Returns the new state. |
rehydrate() | () => void | Re-read localStorage and fire scripts for the granted categories. No event broadcast. |
hasConsent(category) | ('necessary' | 'analytics' | 'marketing' | 'preferences') => boolean | Convenience read of the current state. |
init() | () => void | Bootstrap: rehydrate + register consent:updated listener. Idempotent — safe to call multiple times. |
Configuration
The composable reads app.config.xAffiliate.tracking:
// app.config.ts
export default defineAppConfig({
xAffiliate: {
tracking: {
// ---- Convenience IDs ----
gtmId: 'GTM-XXXXXXX',
ga4Id: 'G-XXXXXXXX',
clarityId: 'abc123def4',
// ---- Escape hatch: arbitrary scripts ----
scripts: [
{
id: 'meta-pixel',
src: 'https://connect.facebook.net/en_US/fbevents.js',
category: 'marketing',
attrs: { async: '' },
},
{
id: 'meta-pixel-init',
inline: 'fbq("init", "1234567890"); fbq("track", "PageView");',
category: 'marketing',
},
],
// ---- Behavior ----
autoInject: true, // default
},
},
})
| Key | Type | Default | Description |
|---|---|---|---|
gtmId | string | — | Google Tag Manager container ID. Fires the analytics category. |
ga4Id | string | — | Google Analytics 4 measurement ID. Skipped when gtmId is set (avoids double-counting). |
clarityId | string | — | Microsoft Clarity project ID. Skipped when gtmId is set. |
scripts | TrackingScript[] | [] | Arbitrary scripts (Meta Pixel, Hotjar, Segment, etc.). Each fires when its category is granted. |
autoInject | boolean | true | When false, no scripts are injected (the composable still tracks state). |
TrackingScript:
| Field | Type | Description |
|---|---|---|
id | string | Stable ID — used to dedupe re-injection. Required. |
src | string | External script URL. |
inline | string | Inline script body (no src). |
category | 'necessary' | 'analytics' | 'marketing' | Category gate. Default: 'analytics'. |
attrs | Record<string, string> | Extra attributes applied to the injected <script> (e.g. { async: '' }). |
Dedupe and GTM-awareness
- The composable dedupes by
id— callingacceptAll()multiple times won't inject the same script twice. - When
gtmIdis set, GTM owns GA4 and Clarity. The composable skips the standalonegtag/js?id=G-XXXand Clarity snippets to avoid double-counting. SetgtmIdalone if you want everything funneled through GTM.
Storage key
Defaults to xAffiliate.consent. Override via the banner's storageKey prop, or by writing/reading the key directly if you need to coordinate with another consent UI (e.g. the marketing layer's <XMarkPrivacyCookieConsent /> uses xMarketing.consent).
Events
| Event | Payload | Fired when |
|---|---|---|
consent:updated | { necessary, analytics, marketing, preferences } | After setConsent (programmatic) or acceptAll / rejectAll. |
The composable itself listens for consent:updated to keep state reactive and re-fire scripts when consent changes.
AI Context
composable: useXAFConsentTracking
package: "@xenterprises/nuxt-x-affiliate"
use-when: >
Wiring consent-aware script injection for GTM / GA4 / Clarity / Meta Pixel /
Hotjar / Segment / LinkedIn Insight / etc. The composable is auto-imported
in Nuxt and listened to via the consent:updated window event. Most apps
should drop tracking IDs into app.config.xAffiliate.tracking and let
<XAFCookieConsent /> handle the rest — reach for the composable directly
only for programmatic grant/reject flows or to read state.
storage-key: xAffiliate.consent
event: consent:updated
scripts-fire-on: matching category grant
useXafLocale
Reactive locale state composable. Reads from a parent XAFLocaleProvider via inject, falls back to a local singleton. Includes a small t() helper for tiny strings.
useXAFContentQuery
Source-agnostic paginated content query state manager — plus typed wrappers useXAFReviews / useXAFArticles / useXAFCategories and a debounced useXAFSearch.
