libconsent
Adapters

IAB TCF

Any TCF v2.2 CMP, through the __tcfapi all of them expose.

npm i @libconsent/tcf
import { ConsentProvider } from "@libconsent/core";
import { tcf } from "@libconsent/tcf";

<ConsentProvider adapter={tcf()}>{children}</ConsentProvider>;

Every CMP registered with the IAB Transparency & Consent Framework has to expose the same window.__tcfapi. This adapter talks to that and nothing else, so one package covers Sourcepoint, Quantcast Choice, Didomi, Google's own CMP, Sirdata, InMobi and the rest of the TCF long tail.

It does not load your CMP

There is no script to add — the adapter reads whichever TCF CMP is already on the page. Keep loading yours however you do today.

Options

OptionDefault
purposessee belowRemap TCF purpose ids onto categories. Merged with the defaults.
specialFeaturesnoneFold special features into categories. Empty by default — see below.
strategy"all"Whether a category needs every purpose behind it, or just one.
legitimateInteresttrueCount purposes established under legitimate interest as granted.
vendors—Map your service ids onto Global Vendor List ids.
commands—TCF has no "open the UI" call. Wire your CMP's own in.

Purposes

TCF v2.2's eleven purposes, and what each one gates here:

#PurposeCategory
1Store and/or access information on a deviceessential
2Use limited data to select advertisingmarketing
3Create profiles for personalised advertisingmarketing
4Use profiles to select personalised advertisingmarketing
5Create profiles to personalise contentfunctional
6Use profiles to select personalised contentfunctional
7Measure advertising performancemarketing
8Measure content performanceanalytics
9Understand audiences through statisticsanalytics
10Develop and improve servicesanalytics
11Use limited data to select contentfunctional

A category is granted when every purpose behind it was consented to. That is the safe reading — a user who allowed audience measurement but refused profiling has not accepted analytics — but if your vendors only need one of the ad purposes, say so:

tcf({ strategy: "any" });

Or move a purpose somewhere else entirely:

tcf({ purposes: { 10: "functional", 11: "marketing" } });

Special features

The two special features — precise geolocation and active device scanning — are a separate question from the purposes, and they stay out of the categories by default. A user may happily accept advertising while refusing a location prompt, and folding that refusal into marketing would switch off every ad script on the page.

They are reported as services instead, so you gate exactly what needs them:

<CookieService id="geo-ads" aliases={["special-feature-1"]}>
  <GeotargetedAds />
</CookieService>

If your vendors genuinely cannot run without one, map it onto a category. A mapping can only take a category away, never grant one:

tcf({ specialFeatures: { 1: "marketing" } });

Outside the EEA

When the CMP reports gdprApplies: false there is nothing to consent to and the banner never shows. Every category is granted, rather than blocking every script for users the framework does not cover.

Vendors

Consent for individual vendors is reported under their Global Vendor List id, both bare and prefixed:

<CookieService id="google-ads" aliases={["755", "vendor-755"]}>
  <GoogleAds />
</CookieService>

Naming ids in every component gets old, so map them once:

tcf({
  vendors: {
    "google-ads": 755,
    "acme-dsp": [12, 34], // both have to be allowed
  },
});

No default map ships with the adapter: GVL ids change as vendors register and deregister, and a stale one silently gates the wrong thing.

Opening the settings

TCF describes consent; it has no call to show a dialog. That is your CMP's own API, so pass it through:

tcf({
  commands: {
    openSettings: () => window._sp_.gdpr.loadPrivacyManagerModal("123456"),
  },
});

Anything you pass shows up in useConsent().supports, so a "Cookie settings" link can check before rendering itself.

Prefer a dedicated adapter when one exists — Didomi reports per-vendor consent and can open its own preference centre, neither of which TCF alone gives you.

The adapter cannot tell which CMP is behind the API, and most TCF CMPs write the Consent Mode signals themselves. libconsent writes them too by default, since both derive from the same consent and a missing signal is worse than a duplicate one. Turn it off if you would rather not:

<ConsentProvider adapter={tcf()} consentMode={false}>

On this page