Consenti

UI Widget — API Methods

After creating a ConsentiSetup instance, the returned object exposes these methods. Only methods the developer needs to call are public — internal state is protected.

Quick reference

MethodReturnsDescription
Consent state
hasConsent()booleanTrue if a valid consent record exists for the current profile
getConsent(type?)ConsentValue | Record<string, string> | nullRaw consent keyed by cookie ID, or pass type ('purpose', 'category', 'google-gtm', 'adobe', 'meta', 'microsoft-clarity', 'twilio-segment') for a re-shaped format
getGTMConsent()Record<string, string> | null@deprecated — same as getConsent('google-gtm')
getConsentDate()Date | falseDate of most recent consent submission, or false if none
isCookieGranted(cookieId, requestValue?)boolean | ConsentStatusCheck if a single cookie is granted. Pass true as second arg to get the raw status string instead of a boolean
isCategoryGranted(categoryId, requestValue?)boolean | {[id: string]: ConsentStatus}[]True when every cookie in the category is granted. Pass true to get an array of per-cookie status records
Bulk actions
grantAll(onlyMandatory?)Promise<void>Accept all cookies. Pass true to grant only mandatory and deny the rest
denyAll(includingMandatory?)Promise<void>Deny all non-mandatory cookies. Pass true to deny mandatory too (logs a warning)
Visibility
showBanner(gpc?)voidProgrammatically show the main (or GPC) banner
hideBanner()voidHide the banner
showModal()voidOpen the preference modal
hideModal()voidClose the preference modal
bannerVisibility()'main' | 'gpc' | falseCurrent banner state
modalVisibility()'preference' | falseCurrent modal state
Events
on(event, handler)voidSubscribe to a typed widget event. The consenti: prefix is optional
off(event, handler)voidUnsubscribe a previously registered handler (pass the same function reference)
Lifecycle
init()Promise<void>Manually start initialisation — required when autoInit: false
onReady(cb)voidRegister a callback fired when the widget is fully initialised
switchLocale(locale)voidSwitch the active locale and re-render the widget
submitConsent(consent)Promise<void>Submit consent programmatically
deleteConsent()Promise<void>Delete consent record (cookie + backend)
reConsent()Promise<void>Delete consent and re-open the banner
forgetMe(resetAgeGate?)Promise<void>Right-to-erasure entry point — wraps reConsent() with consenti:forgetMeRequested/consenti:forgotten events around it. See the Right to Erasure guide.
destroy()voidUnmount the widget and remove all event listeners
Identity
getUserId()string | nullCurrent logged-in application user ID, or null for an anonymous visitor
setUserId(userId, reConsent?)Promise<void>Set (or clear with null) the logged-in application user ID; reconsents by default when it changes on a device with an existing consent record
getVisitor(){ visitorId, type, userId }Snapshot of the current visitor's identity — the stable per-browser visitorId (nulluntil a consent decision exists), whether they're 'authenticated' or 'anonymous', and the app userId
Runtime configuration
setDarkMode(enable?)voidToggle or set dark mode without re-initialising. Omit enable to toggle
setTheme(theme)voidMerge CSS token overrides into the current theme at runtime
setConfig(config)voidDeep-merge a partial config. Re-applies theme/dark mode side-effects; does not re-init
setProfile(override)voidMerge a partial profile override and re-render visible UI without a network call
Diagnostics
version(){ package, profileVersion, consentVersion }Package version, active profile ID, and consent schema version

Method details

init()

Manually triggers widget initialisation. Normally called automatically on construction. Set autoInit: false in ConsentiConfig to disable auto-start — for example when the mount point (rootEl) is rendered after the widget is created.

ts
const widget = new ConsentiSetup({
  core: {},
  rootEl: '#consent-mount',
  autoInit: false,
})

// Later, once #consent-mount is available in the DOM:
await widget.init()
widget.onReady(() => {
  console.log('Widget ready:', widget.hasConsent())
})

init() is also useful after destroy() to re-initialise the same instance rather than creating a new one.

ts
widget.destroy()

// Swap config or wait for a condition, then:
await widget.init()
ℹ️Calling init() while a previous call is still in progress is a no-op — only one initialisation can run at a time per instance.

switchLocale(locale)

Switches the active locale, re-resolves the profile, and re-renders the widget with the new language. The locale switcher UI calls this automatically when the user picks a language. You can also call it programmatically.

ts
widget.switchLocale('fr')   // switch to French
widget.switchLocale('de-AT') // switch to Austrian German

The profile must have multiple locales configured for the switcher to be useful. See Advanced Configurationcore for locale setup details.

onReady(callback)

Called once the profile has resolved and the banner state has been determined. Safe to call before or after the widget has initialised.

ts
widget.onReady(() => {
  console.log('Widget ready. Has consent:', widget.hasConsent())
})

hasConsent()

ts
if (!widget.hasConsent()) {
  widget.showBanner()
}

getConsent(type?)

With no argument, returns the raw consent map — keyed by whichever cookie parameterIDs actually exist in the profile. There's no fixed/predefined key set; the shape mirrors the profile's own cookies map one-for-one:

ts
const consent = widget.getConsent()
// keyed by your own cookie parameter IDs, e.g.:
// { ga_measurement: 'granted', hotjar: 'denied', preferences_storage: 'granted' }

if (consent?.ga_measurement === 'granted') {
  initAnalytics()
}

Pass type to get a re-shaped, vendor- or taxonomy-ready object instead. Every value below is a real, independent ConsentType — none of them are aliases of each other:

typeKeyed byValues
'purpose'the fixed taxonomy: necessary/functional/preferences/analytics/marketing'granted' | 'denied' | 'objected'
'category'your own authored category IDs (preferenceModal.categories keys)'granted' | 'denied' | 'objected'
'google-gtm'Google Consent Mode v2 signal names'granted' | 'denied' + ads_data_redaction/url_passthrough flags
'adobe'analytics, target, manager, optimizer'granted' | 'denied' | 'objected'
'meta'pixel, api, plugins, facebookLogin'granted' | 'denied' | 'objected'
'microsoft-clarity'session, heatmaps, performance'granted' | 'denied' | 'objected'
'twilio-segment'identify, page, track, group, alias'granted' | 'denied' | 'objected'

'purpose' and 'category' are easy to mix up — they answer different questions. 'purpose' gives you the fixed, stable taxonomy every parameter is tagged with (useful for vendor mapping, which is why the formats below all derive from it too). 'category' gives you consent for your own authored category IDs instead — 'granted' only when every parameter in that category is granted; otherwise 'denied' (covers a fully-denied category and a partially-granted one alike) or 'objected' (only for a legitimate_interestcategory that's uniformly objected to):

ts
widget.getConsent('purpose')
// { necessary: 'granted', functional: 'granted', preferences: 'denied', analytics: 'denied', marketing: 'denied' }

widget.getConsent('category')
// { 'cat-necessary': 'granted', 'cat-analytics': 'denied', 'cat-personalization-li': 'objected' }
// — keyed by whatever category IDs you defined in preferenceModal.categories

See Advanced Profiles for the full parameter (purpose) vs. category model.

getGTMConsent()

⚠️@deprecated — use getConsent('google-gtm') instead; identical output. Kept for backwards compatibility.

Returns consent in the exact shape Google Tag Manager expects for Consent Mode v2:

ts
const gtm = widget.getGTMConsent()
// {
//   analytics_storage: 'granted',
//   ad_storage: 'denied',
//   ad_user_data: 'denied',
//   ad_personalization: 'denied',
//   functionality_storage: 'granted',
// }

For the other vendor formats ('adobe', 'meta', 'microsoft-clarity', 'twilio-segment'), see the respective Adobe, Meta, Microsoft Clarity, and Twilio Segment integration guides.

submitConsent(consent)

Programmatically submit consent — useful for custom UI flows:

ts
await widget.submitConsent({
  analytics: 'granted',
  marketing: 'denied',
  necessary: 'granted',
})
⚠️Cookies belonging to a category with legalBasis: 'mandatory' are always 'granted' regardless of what you pass. Passing 'denied' for one of them is silently ignored.

reConsent(resetAgeGate?)

Deletes the existing consent record and re-opens the banner. Use for "Change cookie settings" buttons:

ts
document.querySelector('#change-cookie-settings')?.addEventListener('click', () => {
  widget.reConsent()
})

If the profile has an ageGate configured, reConsent() re-shows the age-gate prompt too by default (not just the banner) — mirroring the original first-visit flow. Pass falseto skip it and go straight to the banner, keeping the visitor's prior age-gate answer:

ts
widget.reConsent(false) // skip the age gate, just re-show the banner

forgetMe(resetAgeGate?)

The self-service "right to be forgotten" entry point — GDPR Art. 17, CCPA/CPRA, LGPD Art. 18, and equivalent erasure rights. Same underlying erasure as reConsent() (same resetAgeGate parameter), but dispatches consenti:forgetMeRequested before and consenti:forgottenafter, so a host app can hook its own identity-verified erasure workflow across other systems (CRM, DMP, analytics) — the CMP itself only ever erases its own consent record. This is what powers the preference modal's "Forget me" button (preferenceModal.showForgetMe), and is also the method to call for a custom placement of the same action — a footer link or an account/privacy-settings page:

ts
document.querySelector('#delete-my-data')?.addEventListener('click', () => {
  widget.forgetMe()
})

See the Right to Erasure guide for the full picture, including the server-side DELETE /consent/:visitorId endpoint and its own consent:erased eventBus event.

isCookieGranted(cookieId, requestValue?)

The most common gating pattern — check whether a single cookie is 'granted'without manually inspecting getConsent().

ts
// Boolean mode (default) — true if granted
if (widget.isCookieGranted('analytics_storage')) {
  initAnalytics()
}

// Value mode — returns the raw ConsentStatus string, or false if not in the consent map
const status = widget.isCookieGranted('marketing', true)
// 'granted' | 'denied' | 'objected' | false

During SSR or before init() completes, always returns false.

isCategoryGranted(categoryId, requestValue?)

Category-level consent check. The categoryId must match a key in the profile's preferenceModal.categories map.

ts
// Boolean mode — true only when ALL cookies in the category are 'granted'
if (widget.isCategoryGranted('cat-analytics')) {
  loadHeatmaps()
}

// Value mode — one record per cookie in the category
const statuses = widget.isCategoryGranted('cat-marketing', true)
// [{ ad_storage: 'granted' }, { ad_personalization: 'denied' }]

Returns false / [] if the category ID is not found or the widget is not yet initialised.

grantAll(onlyMandatory?)

Programmatically accept cookies without the user interacting with the banner. Dismisses the banner after submitting.

ts
// Accept everything
await widget.grantAll()

// Accept only mandatory cookies — deny the rest (useful for "Reject Optional" variants)
await widget.grantAll(true)

denyAll(includingMandatory?)

Programmatically deny all non-mandatory cookies. Dismisses the banner after submitting.

ts
// Deny non-mandatory; mandatory cookies stay 'granted'
await widget.denyAll()

// Deny everything including mandatory — use with caution
await widget.denyAll(true)
⚠️Passing true to denyAll denies mandatory cookies. This is intentionally allowed for testing and special integrations, but will log a console.warn as a reminder.

on(event, handler) / off(event, handler)

Typed event subscription API — a cleaner alternative to window.addEventListener. The consenti: prefix on the event name is optional; both forms are accepted.

ts
import type { ConsentEvent } from '@consenti/ui'

const handler = (data: ConsentEvent) => {
  console.log('Consent saved:', data.consentJson)
  console.log('Action:', data.consentAction) // 'accept_all' | 'reject_all' | 'custom' | 'update'
}

// Subscribe — both are equivalent
widget.on('consentSubmitted', handler)
widget.on('consenti:consentSubmitted', handler)

// Unsubscribe — must pass the same function reference
widget.off('consentSubmitted', handler)

// All supported event names:
// 'bannerInitialized'       — widget initialised; hasExistingConsent is in the detail
// 'bannerVisibility'        — banner showed or hid; show / action flags in detail
// 'modalVisibility'         — modal opened or closed
// 'consentBeingSubmitted'   — user clicked a button (before API call)
// 'consentSubmitted'        — consent saved (cookie written + API call if configured)
ℹ️on() / off() use the same underlying DOM events as raw window.addEventListener calls. Both can coexist in the same page. Registered handlers are automatically cleaned up when destroy() is called.

setDarkMode(enable?)

Toggle or set dark mode at runtime without re-initialising the widget. The dark class is applied to both the banner root and the preference modal.

ts
widget.setDarkMode()       // toggle current state
widget.setDarkMode(true)  // force dark
widget.setDarkMode(false) // force light

// Typical use: follow the OS preference and update live
const mq = window.matchMedia('(prefers-color-scheme: dark)')
widget.setDarkMode(mq.matches)
mq.addEventListener('change', (e) => widget.setDarkMode(e.matches))

setTheme(theme)

Merge CSS token overrides into the current theme at runtime. CSS custom properties on the root element update immediately — no page reload required.

ts
// Switch primary colour on the fly (e.g. white-label tenant switch)
widget.setTheme({ colorPrimary: '#d32f2f', colorPrimaryText: '#ffffff' })

// Only the provided keys are updated — other theme values are preserved
widget.setTheme({ borderRadius: '0px' })

setConfig(config)

Deep-merges a partial ConsentiConfig into the current config without re-initialising. Theme and dark mode side-effects are re-applied immediately. For locale or profile changes, follow up with switchLocale() or init().

ts
// Update theme and dark mode together
widget.setConfig({
  darkMode: true,
  core: { theme: { colorPrimary: '#1a73e8' } },
})

// Disable powered-by branding at runtime
widget.setConfig({ hidePoweredBy: true })

setProfile(override)

Merges a partial profile override into the active profile and re-renders any currently visible banner or modal — no network call is made. Useful for A/B testing copy or dynamically adjusting the banner text after init.

ts
// Change the banner heading live
widget.setProfile({
  mainBanner: { heading: 'We care about your privacy' },
})

// Swap the modal position
widget.setProfile({
  preferenceModal: { position: 'left' },
})
ℹ️setProfile is a no-op before init() completes — the resolved base profile must exist for the merge to run.

version()

Returns the package version and the currently active profile ID. Useful for support diagnostics and feature flags.

ts
const info = widget.version()
// {
//   package: '0.1.1',              — npm package version
//   profileVersion: 'a1b2c3d4-…',  — the active profile's id (a new id is minted on every profile edit, so this doubles as a change indicator)
//   consentVersion: null,          — reserved for a future consent schema version
// }
console.log(`Consenti ${info.package} | profile ${info.profileVersion}`)

destroy()

Removes the banner DOM, modal DOM, BroadcastChannel listener, and all event listeners (including those registered via on()). Call when unmounting in SPA route changes if you manage the lifecycle manually.

ts
// In a SPA router's cleanup callback:
widget.destroy()

getUserId() / setUserId(userId, reConsent?)

Ties the widget's consent record to your own logged-in application user, so consent follows the visitor across devices and browser sessions instead of resetting every time they sign in somewhere new. Call setUserId() after your own auth flow resolves (on login) and again with null on logout:

ts
// After your app's login completes:
await widget.setUserId('user_123')

// On logout:
await widget.setUserId(null)

// Read it back anywhere:
widget.getUserId() // 'user_123' | null

If the browser already has a stored consent record for a different user ID (a shared device), setUserId() treats that as a different data subject: by default it deletes the existing record and re-opens the banner (reConsent()), and always logs a warning. Pass reConsent: false to just update the identity without prompting again — the warning still logs, so you have a record of the identity change:

ts
await widget.setUserId('user_456', false)

Calling setUserId()with the value it's already set to is a no-op, and if there's no prior consent record to compare against (a true first-time visitor), it just records the ID with no warning and no reconsent. You can also set the initial user ID at construction time via core.userId, or drive identity from an existing event bus by dispatching a consenti:listener:identify DOM event — see Advanced Configurationcore.

getVisitor()

A single snapshot of the current visitor's identity — useful for logging, debugging, or deciding whether to call setUserId() in the first place:

ts
const visitor = widget.getVisitor()
// {
//   visitorId: 'visi_a1b2c3d4…' | null, — stable per-browser id
//   type: 'authenticated' | 'anonymous',
//   userId: 'user_123' | null,          — same value as getUserId()
// }

type is 'authenticated' whenever a userId is set, 'anonymous' otherwise. visitorId is null until this browser has actually made a consent decision — Consenti never mints or persists a visitor identifier just because getVisitor() was called, only as a side effect of a real consent decision (manual submit, GPC auto-response, or a decision relayed from another tab). Once a decision exists, the same visitorId is also included in the consenti:consentSubmitted event payload and every server-side consent record.