Skip to main content

JavaScript API

@c15t/browser API

Entry points

ImportWhat it gives you
@c15t/browserinit, createConsentClient and mountConsentUI, with the stock banner, dialog and trigger. mode is required: a factory from manifest(), hosted(), offline() or custom().
@c15t/browser/hostedThe stock UI and client for hosted mode only. Requires a backend URL or a hosted transport factory.
@c15t/browser/offlineThe stock UI and client for offline mode only, with bundled policy rules and presets.
@c15t/browser/headlessThe same client with no UI and no CSS. mountUI() throws.
@c15t/browser/iabThe client with the IAB TCF CMP and the IAB banner and dialog. See IAB TCF.
@c15t/browser/devtoolsmountDevTools(client, options). See DevTools.
@c15t/browser/styles.css, @c15t/browser/iab/styles.cssThe stylesheets, for ui: { shadow: false, styles: false }.

Install it with:

npm install @c15t/browser@alpha

In the ES module build the preference dialog code is a separate chunk. It starts loading in idle time once the banner or trigger is on screen, and at the latest when the dialog opens. Idle preloading can download it even when the visitor never opens preferences.

The script-tag files c15t.js, c15t.offline.js and c15t.iab.js each carry their complete stylesheet in one file. The default c15t.js uses the hosted client and requires a backend URL or hosted factory. c15t.offline.js uses the offline client. For manifest or custom mode with the stock UI, use the ES module root @c15t/browser, which takes any mode factory.

@c15t/browser is not part of the c15t package. Use one entry per page. Each creates its own client, and two clients on one page would each run the script loader and blockers.

Choose a bundle for one mode

Use @c15t/browser/hosted when Inth or a self-hosted backend supplies the policy. It excludes offline policy presets, offline resolution and manifest transport code. Hosted mode already resolves the visitor's policy on the server through /init; this entry removes unused client code without changing that request.

Use @c15t/browser/offline when the browser resolves bundled rules. It excludes hosted and manifest transport code. The default offline transport keeps choices in the browser's consent cookie and localStorage, with no consent backend requests or server records. Not recommended for production environments.

Both entries keep the banner, preference dialog, page hooks, script gating, blockers, callbacks and persistence. Their init() and createConsentClient() methods have the same lifecycle as the general entry.

EntryAccepted configurationConfiguration that throws
@c15t/browser/hostedbackendURL, optional mode: 'hosted', or a factory from hosted(). policyRules accepts authored PolicyRule objects.Missing backend URL without a hosted factory; a different mode; manifest or manifestURL; preset-name strings in policyRules.
@c15t/browser/offlineOptional mode: 'offline' or a factory from offline(). policyRules accepts authored rules and preset names, or defaults to the recommended rules.backendURL, manifest or manifestURL; a different mode.

Use @c15t/browser for manifest or custom mode, or when an application chooses its mode at runtime. Use @c15t/browser/iab for IAB TCF. The general entry imports no transport itself, so a bundle keeps only the factory it imports. It takes mode as a factory only: backendURL, manifest, manifestURL, policyRules and mode names such as 'hosted' are options of the /hosted and /offline entries and the script-tag builds.

Start the client

init(options) creates the client and starts it:

src/main.ts
import { init, manifest } from '@c15t/browser';
import { posthog } from '@c15t/integrations/posthog';

init({
	mode: manifest(),
	scripts: [
		posthog({
			id: 'phc_your_project_key',
			initOptions: { cookieless_mode: 'never' },
			loadMode: 'after-consent',
		}),
	],
});

createConsentClient(options) creates the client without starting it. Construction reads no storage, sends no request and touches no DOM, so a module can create it at import time and call client.start() later:

import { createConsentClient, manifest } from '@c15t/browser';

export const consent = createConsentClient({ mode: manifest(), scripts });

start() reads stored choices, requests the policy, starts the script loader, the iframe blocker and any network blocker, listens for page hooks and mounts the UI. It mounts on DOMContentLoaded when <body> does not exist yet. A second start() does nothing. Options lists everything init and createConsentClient accept.

Read state

MemberReturnsUse it for
ready()Promise<ConsentSnapshot>Wait until the policy has resolved. For a returning visitor, hasConsented() is reliable after it.
has(condition)booleanWhether a category, or a condition such as { or: ['measurement', 'marketing'] }, is allowed right now.
hasConsented()booleanWhether the visitor has answered the choice prompt.
getSnapshot()ConsentSnapshotThe full state. See the snapshot reference.
subscribe(listener)() => voidCall listener with each new snapshot. Returns an unsubscribe function.
on(event, listener)() => voidListen for ready, consent, ui or error.
consentCategoriesAllConsentNames[]The categories the dialog offers, in a fixed order: necessary, functionality, measurement, experience, marketing, limited to the policy's categories. The order of the consentCategories option doesn't change it.

has() reads a permission, which says whether code may run now. Under an opt-out policy it is true before the visitor does anything. hasConsented() and getSnapshot().explicitChoice read the recorded choice, which says what the visitor decided. Gate code on permissions; report on recorded choices. How consent works explains the difference.

Record a choice

Call these only from a visitor's click or key press.

MethodReturnsWhat it records
acceptAll()Promise<SaveResult>Without IAB, allows every category the dialog offers. Under an IAB policy it confirms the vendors' declared processing through the CMP. Optional categories without declared consent purposes remain denied. See categories under an IAB policy.
rejectAll()Promise<SaveResult>Denies every optional category the dialog offers.
save(consents)Promise<SaveResult>The categories you name, such as { measurement: true }. Categories the dialog does not offer are dropped. Under an IAB policy it resolves { ok: false } and emits error; use saveIAB().
saveIAB()Promise<SaveResult>The IAB purpose and vendor choices on runtime.iab. IAB entry only.
dismissNotice()Promise<NoticeDismissResult>Acknowledges a notice. Grants nothing. { ok: false, reason: 'not-required' } when no notice is due.
identify(user)Promise<void>Links the consent record to { externalId, identityProvider?, identityToken? }; the token verifies the link. Failures surface through error.

Each save applies in the browser and closes the banner or dialog before the backend request finishes. SaveResult.ok is false when the request failed; the choice stays in effect and c15t retries the request later.

Control the UI

MethodWhat it does
showBanner(), openDialog(), closeDialog()Change the surface c15t wants shown. Records nothing.
setLanguage(code)Sets the language and resolves the policy again, which fetches that language's copy from the backend. In offline mode the copy comes from the bundled translations and i18n.messages. See translations.
setOverrides(overrides)Merges into the country, region, language and GPC inputs to policy matching. A key you pass replaces its value, undefined clears it, and a key you leave out stays.
mountUI(options?)Removes the stock UI and mounts it again with new UI options. Throws in the headless entry.
processIframes()Pauses gated iframes that consent does not allow and restores the ones it allows. Needed only with iframeBlocker: { disableAutomaticBlocking: true }. See iframe blocker.
uiThe mounted UI handle, with host, root, update() and destroy(), or null.

Lifecycle and internals

MemberWhat it is
start()Starts the client. init() calls it for you.
startedWhether start() has run and the client is not disposed.
dispose()Removes the UI, page listeners, blockers and the scripts c15t added, then disposes the runtime. Code that already ran keeps running.
optionsThe options the client was created with.
modehosted, offline, manifest or custom.
runtimeThe consent runtime that owns the kernel and modules. runtime.iab is the CMP handle under IAB.
kernelThe consent kernel: snapshot, commands and events.

Do not attach a second script loader, network blocker or iframe blocker to client.kernel. The client already runs one of each.

Events

on(event, listener) returns an unsubscribe function:

EventPayloadWhen it fires
readythe snapshotThe policy has resolved and the UI can render. For a returning visitor, hasConsented() is reliable from here.
consentthe snapshotPermissions, the recorded choice or the vendor switches changed. This includes a stored choice restored at start, a save, an expiry and a GPC change.
ui'banner', 'dialog' or 'none'The surface c15t wants shown changed.
errorthe errorA backend call failed, or an IAB step failed.

A ready listener added after the policy resolved runs at once with the snapshot from that moment. A ui listener added after that runs at once with the current surface. consent and error listeners only hear later changes.

Each event is also dispatched on document as c15t:ready, c15t:consent, c15t:ui and c15t:error, with the payload in event.detail. Callbacks compares events with the callbacks option.

Page hooks

A started client also wires markup on the page, with no code:

MarkupWhat it does
<button data-c15t-action="…">Runs the action on click. Values below.
A link whose href ends in #c15t-preferencesOpens the preference dialog.
<script type="text/plain" data-c15t-category="…">Runs the script once that category is allowed, in page order.
<iframe data-src="…" data-category="…">Sets src once that category is allowed.
ValueWhat a click does
acceptLike the banner's accept button, allows every category the dialog offers without IAB. Under an IAB policy it confirms the vendors' declared processing through the CMP. Optional categories without declared consent purposes remain denied. See categories under an IAB policy.
rejectRejects every optional category the dialog offers.
customizeOpens the preference dialog. Records nothing.
dismissDismisses a notice. This records an acknowledgement, not consent, and does nothing unless the policy shows a notice.
bannerShows the banner again. Records nothing.
closeCloses the banner or dialog. Records nothing.

Helper exports

The general @c15t/browser entry exports all the helpers below. The hosted entry exports hosted; the offline entry exports offline and resolveRules. Both include the page action constants, gated script helpers, mountConsentUI and version.

ExportWhat it does
hosted, offline, manifest, customTransport factories for the mode option. See transports.
manifestNeedsLocation(manifest)Whether some location gets a different banner, or none, under this manifest, so a page knows it must supply a country to resolve it without /init. false when every country and region rule gives the same banner.
resolveRules(rules)Turns preset names such as 'europeOptIn' into policy rules. Throws on an unknown name.
activateGatedScripts(snapshot, root?, options?)Runs allowed text/plain scripts under root yourself, for a setup without a client. options.nonce runs only tags that carry that nonce. Returns how many ran.
mountConsentUI(client, options?)Mounts the stock UI for a client. mountUI() calls it.
ACTION_ATTRIBUTE, PREFERENCES_HASH, CATEGORY_ATTRIBUTE, ACTIVATED_ATTRIBUTE'data-c15t-action', '#c15t-preferences', 'data-c15t-category' and 'data-c15t-activated'.
versionThe package version.

The package also exports the ConsentClient, ConsentClientOptions, ConsentUIOptions, ConsentBannerOptions, ConsentDialogOptions, ConsentTriggerOptions, ConsentClientEventMap, ConsentSnapshot and ConsentState types.

The hosted and offline entries export HostedConsentClientOptions and OfflineConsentClientOptions, respectively. These types restrict connection options to the entry's mode. Runtime validation also checks a supplied transport factory's kind.

Check it works

  1. Start the app and run await consent.ready() from code or a breakpoint. It resolves with a snapshot whose resolution.status is matched.
  2. Click Reject All. consent.hasConsented() is true and consent.has('measurement') is false.
  3. Call consent.openDialog(). The preference dialog opens with Analytics (the measurement category) off.