HTML Advanced
IAB TCF
Load the IAB build
Load c15t.iab.js instead of c15t.js when your policy uses IAB TCF. It
contains the CMP, the TC string encoder, the IAB banner and the purpose and
vendor preference dialog. c15t.js, c15t.offline.js and
c15t.headless.js leave all of that out, so pages without an IAB policy stay
smaller. The stock banner on those builds stands aside for a policy that
uses the iab model, so a page that resolves one emits an error event and
throws an IABUnavailableError (code C15T_IAB_UNAVAILABLE) as an uncaught
error. So does c15t.iab.js with iab: false. Load c15t.iab.js for
visitors whose policy uses IAB TCF, or remove the iab model from the
policy.
Replace YOUR_CMP_ID with the ID IAB Europe assigned your CMP at
registration. Every TC string names the
CMP that wrote it, so never copy an ID from an example. A backend can supply
cmpId and the vendor list instead. vendors limits the list to the vendors
your site works with.
Load only one c15t bundle. c15t.iab.js throws an error if c15t.js,
c15t.offline.js or c15t.headless.js loaded first. When the resolved policy is not an IAB
policy, the IAB build shows the ordinary banner. If an IAB policy cannot load
its vendor list, the banner shows an error and keeps its confirm buttons
disabled.
A self-hosted backend serves this build at
/c15t.iab.js. Set script.config.iab on the backend to supply CMP defaults.
Confirm IAB choices from your code
c15t.acceptAll() and c15t.rejectAll() confirm through the CMP when the
policy is IAB. For individual choices, change the draft on the CMP handle and
confirm it with c15t.saveIAB():
ready() waits for the policy. cmp.whenReady() waits for the vendor list and
CMP setup, and rejects if setup fails. The handle also has
setPurposeLegitimateInterest, setVendorLegitimateInterest and
setSpecialFeatureOptIn. Each edits the draft; saveIAB() confirms it.
c15t.save({ ... }) with categories fails under an IAB policy, because it
cannot produce a TC string.
The CMP installs window.__tcfapi. c15t.dispose() removes it.
How the stock dialog groups choices
In the stock preference dialog, turning on a purpose also gives consent to the vendors that use that purpose on a consent basis. A purpose's legitimate-interest control changes the purpose and its legitimate-interest vendors, and turning it off records an objection. A stack's control covers its purposes and their consent-based vendors, and shows a mixed state when only some are on. The vendor tab has search, per-vendor consent and legitimate-interest controls, and each vendor's disclosures from the Global Vendor List.
The runtime.iab setters each change only the choice they name. A custom UI
that wants the same grouping must set the related vendor choices itself.
How the preference centre shows features
The stock IAB preference centres in React, Next.js, Vue, Svelte, Astro and the script tag follow TCF 2.4 and Policies v5.0.b. Special purposes stay in their locked section with a lock icon, because the visitor cannot object to them.
Features get their own section after the special purposes. It shows the IAB
standard text for features from the Global Vendor List's
standardTexts.features. If the list has no standard text, it shows the
iab.preferenceCenter.features.description translation. Each feature lists
its name, description, illustrations and the vendors that use it. The section has no switch, lock or vendor toggle, because
TCF forbids showing features next to a control that cannot be disabled.
The feature section follows the theme's spacing and typography tokens. Its disclosure arrows are decorative and stay out of the accessibility tree.
A custom preference UI gets the same rows from
resolveIABDialogDisplayModel in @c15t/iab/headless. essentialRows holds
the special purposes only. featureRows holds the features, with
locked: false and toggle: 'none', so render them without a control.
featuresStandardText is the standard text, or null when you should use the
translation.
Each vendor's privacy policy and legitimate interest links come from its
urls[] entry in the Global Vendor List. The stock preference centres pick
the entry for the language the banner shows, then English. A custom UI gets
the same links from resolveIABVendorUrls(vendor, language) in
@c15t/iab/headless, or from policyUrl and legitimateInterestUrl on the
vendors processGVLForDialog returns when you pass it language. Vendor lists
older than GVL v3 have a single policyUrl field, which c15t still reads.
Consent scope
c15t stores TCF choices per site and browser, which the TCF calls
service-specific scope. It does not sync TC strings across devices, and every
TC string it writes has IsServiceSpecific set to 1.
Do not use /consent/check or your own account data to skip the TCF banner on
another device. That turns the choice into multi-device scope, which the TCF
requires you to disclose in the first layer, and c15t does not show that
disclosure. The isServiceSpecific option is deprecated. c15t ignores it and
logs a warning when you pass false.
Style and word the IAB UI
The IAB UI takes the same ui.theme, ui.css and ui.shadow options as the
ordinary banner; see customize. With
shadow: false and your own stylesheet, load both dist/c15t.css and
dist/c15t.iab.css from the package.
The IAB banner always shows the c15t branding. ui.dialog.hideBranding hides
it in the preference dialog. ui.banner.title, description and the button
text options apply to the IAB banner too, and a description can include
{partnerCount}. ui.iab.loadErrorText, saveErrorText and
moreVendorsText change the loading error, save error and pagination copy.
Set ui: false to render your own IAB UI against the client. The page still
downloads the IAB build.
Try it locally
The repository's internals/fixtures/script-tag/iab.html runs the IAB build offline with
a sample vendor list. Build @c15t/browser, run
bun run --cwd internals/fixtures/script-tag dev and open /iab. It uses a
demonstration CMP ID; replace it with your own for a real site.