---
title: Composables
description: Every c15t composable the Nuxt module auto-imports, grouped by
  task, with the difference between a permission and a recorded choice.
group: frameworks
lastModified: "2026-10-10T16:01:45+01:00"
---
## Use composables without imports

The Nuxt module auto-imports the consent composables, so a component calls
them directly. This component reads the visitor's permission and recorded
choice, and opens preferences:

```vue title="app/components/ConsentStatus.vue"
<script setup lang="ts">
// May measurement code run right now?
const permissions = useConsent();
// What did the visitor decide? `null` until they accept, reject or save.
const choice = useExplicitChoice();
const activeUI = useConsentActiveUI();
</script>

<template>
	<section aria-label="Your privacy choices">
		<p v-if="choice">You saved your privacy choices.</p>
		<p v-else>You have not made a privacy choice yet.</p>
		<p>
			Measurement is
			{{ permissions.measurement ? 'allowed' : 'not allowed' }} right now.
		</p>
		<button type="button" @click="activeUI = 'manager'">
			Change your choices
		</button>
	</section>
</template>
```

Use `v-if`, not `v-show`, when optional content loads anything from a third
party. For iframes use [`ConsentGate`](/docs/frameworks/nuxt/components/consent-gate),
and for vendor scripts use the `scripts` option in `app.config.ts`. See
[scripts](/docs/frameworks/nuxt/scripts).

A few composables are not auto-imported, among them `useHasConsentPolicy()`,
`useHasConsentUi()`, `useHasConsentPreferences()` and `useIabTranslations()`.
Import them from `#c15t/composables`, the alias the module registers for its
composables:

```ts
import { useHasConsentPreferences } from '#c15t/composables';
```

## Where composables work

Call the composables inside `setup` of a component that renders in the app
where c15t is installed. Anywhere else they throw an error such as
`[c15t] Kernel not found`. Most return a Vue `computed`, so read them with
`.value` in `<script setup>` and without it in the template. They update
whenever the consent runtime changes.

Server routes and middleware under `server/` run outside the Nuxt app, so the
composables do not work there.

## Permission or recorded choice

`useConsent()` and `useHasConsent()` answer "may this run now?" Under an
opt-out policy a category can be allowed before the visitor has done
anything, and Global Privacy Control can deny a category the visitor
allowed. Use them to decide whether to run code or render optional content.

`useExplicitChoice()` answers "what did the visitor decide?" It stays
`null` until the visitor accepts, rejects or saves. Use it to show the
visitor's choice or to report consent rates. Loading a page never changes
it, and dismissing a notice does not set it.

Never save a choice on the visitor's behalf because a permission is `true`.
[How consent works](/docs/concepts/how-consent-works#a-permission-is-not-a-recorded-choice)
explains the difference.

## Read consent state

|Composable|Returns|Use it to|
|--|--|--|
|`useConsent()`|Computed `Record<category, boolean>`|Read every category's current permission, such as `consent.value.marketing`.|
|`useHasConsent()`|Computed array of allowed categories|List what may run now. The array keeps its identity until a category changes.|
|`useEffectivePermissions()`|Computed permissions|The same values as `useConsent()`, read from the snapshot field.|
|`useVendorAllowed(id)`|Computed `boolean`|Check whether one vendor may run: it is declared, its category is allowed and the visitor has not switched it off. `false` for an id nothing declares.|
|`useExplicitChoice()`|Computed recorded choice, or `null`|Read what the visitor decided.|
|`useStoredConsent()`|Computed recorded choice, or `null`|The same value as `useExplicitChoice()`.|
|`useNoticeDismissal()`|Computed dismissal record, or `null`|Check whether the visitor dismissed a notice.|
|`usePrivacySignals()`|Computed privacy signals|Read whether Global Privacy Control was detected and applied.|
|`useConsentRestrictions()`|Computed restrictions|See why a permission is limited, for example by the policy or a privacy signal.|
|`usePolicyRule()`|Computed policy rule|Read the `model`, `prompt`, `scope` and `rights` of the visitor's policy.|
|`usePolicyResolution()`|Computed resolution|Check that a policy matched. `status` is `'matched'` when one applies.|
|`useRequestRegion()`|Computed `{ country, region }`|Read the location the policy resolved for.|

## Composables on the server

The composables work during server rendering. When the server resolves the policy, in the default `manifest()` mode or `hosted()`,
they return the visitor's resolved policy and stored
choice, so the server HTML and
the first client render agree. On prerendered and cached routes, and with
`ssr: false`, they start with every optional category denied and update once
the browser resolves the policy. Until then
`useConsentSnapshot().value.policyPending` is `true`.

## Record a choice

`useConsentSave()` returns a function that records a choice and resolves to
`{ ok }` once the backend has answered. c15t applies the choice in the page
before the request, so gated scripts start without waiting for it.

|Argument|Records|
|--|--|
|`'all'`|Every category the policy covers allowed.|
|`'none'`|Every optional category denied.|
|`['measurement']`|The listed categories allowed and every other optional category denied.|

`useDismissNotice()` returns the function that dismisses a notice. It
records an acknowledgement, grants nothing and leaves earlier refusals in
place. Use it only for a policy whose prompt is `notice`.

## Open the banner or preferences

`useConsentActiveUI()` returns a writable computed. Its value is
`'banner'`, `'manager'` or `null`. Set it to `'manager'` to open
preferences, to `'banner'` to show the banner again, or to `null` to close
both. The Vue adapter uses `'manager'` where the React adapter uses
`'dialog'`. `ConsentDialogLink` does the same as setting `'manager'` and
hides itself when the policy offers no preferences.

## Change the language

`useConsentLanguage()` returns a writable computed with the language
override, or `null`. Assigning a new language code stores it and runs init
again, so c15t fetches the banner and dialog text in that language.
Assigning the current language, or `null`, does nothing.

The `language` prop on the Nuxt
[`ConsentRoot`](/docs/frameworks/nuxt/components/consent-root#props) does the
same from a template binding. See
[translations](/docs/frameworks/nuxt/translations) for a language switcher.

## Build a preference form

`useConsentDraft()` holds the switches of a preference form separately from
the recorded choice, so nothing is saved until the visitor clicks Save.

|Field|Type|Meaning|
|--|--|--|
|`displayedCategories`|`ComputedRef<category[]>`|The categories the policy lets the visitor choose, in the order necessary, functionality, measurement, experience, marketing.|
|`values`|`Ref<Partial<Record<category, boolean>>>`|One value per category. Bind switches to it with `v-model`; a write to `necessary` or to a category the policy does not offer snaps back.|
|`vendors`|`ComputedRef<Record<string, boolean>>`|One value per declared vendor. Change them with `setVendor(id, granted)`.|
|`isDirty`|`ComputedRef<boolean>`|`true` while any value differs from the recorded choice.|
|`isStale`|`ComputedRef<boolean>`|`true` when the policy, the displayed categories or the vendor list changed while the draft held an unsaved change.|
|`save()`|`() => Promise<{ ok: boolean }>`|Records `values` and changed vendors. Resolves `{ ok: false }` without saving while `isStale` is `true`.|
|`reset()`|`() => void`|Refills the draft from the recorded choice and the policy.|

The draft starts from the recorded choice. Before any choice, it starts
from the policy's defaults: on under an opt-out policy or for preselected
categories, otherwise off. A choice saved elsewhere updates the values the
visitor has not touched. `save()` does not close anything. Set `activeUI`
to `null` yourself after it.

## Render the actions the policy requires

|Composable|Returns|Use it to|
|--|--|--|
|`usePromptRequirement()`|Computed `{ kind }`, where `kind` is `'choice'`, `'notice'` or `'none'`|Decide whether to show a banner at all.|
|`useConsentPolicyActions(surface)`|Computed `actionGroups`, `primaryActions`, `variant`, `position`, `blocking`, `direction` and `preferenceControls`|Render the buttons a banner (`'prompt'`) or preference form (`'preferences'`) must show, in order.|
|`useHasConsentPolicy()`|Computed boolean|Check that a policy matched the visitor.|
|`useHasConsentUi()`|Computed boolean|Check that the policy owes any consent UI. Every stock surface hides while it is `false`.|
|`useHasConsentPreferences()`|Computed boolean|Check whether a preferences control should render.|

Under a notice, `actionGroups` holds `dismiss` instead of accept and
reject. `preferenceControls` lists extra buttons, `'opt-out'` or
`'preferences'`, that open preferences under a policy with those rights.

[Headless](/docs/frameworks/nuxt/headless) builds a complete banner and
preference form from these composables.

## IAB TCF

|Composable|Returns|Use it to|
|--|--|--|
|`useConsentIabSelection()`|Writable computed with purpose, legitimate-interest, vendor and special-feature maps, and `preferenceCenterTab`|Read and change the visitor's IAB selection in a custom preference centre. Writing it updates gating live without saving.|
|`useConsentIabSave()`|`(input, tab?) => Promise<void>`|Save `'all'`, `'none'` or a selection. It waits for the Global Vendor List, then writes the TC String.|
|`useIabTranslations()`|Computed IAB copy|Read the IAB banner and dialog text for the visitor's language.|

## Advanced

|Composable|Returns|Use it to|
|--|--|--|
|`useConsentSnapshot()`|`Ref<ConsentSnapshot>`|Read any snapshot field, such as `policyPending`, `vendors` or `iab`.|
|`useConsentKernel()`|The consent kernel|Call `commands.init()`, `commands.save()`, `commands.identify(user)`, subscribe with `events.on(type, listener)`, or read `getSnapshot()`.|
|`useConsentConfig()`|Computed options, merged with defaults|Read the options you passed.|
|`useConsentInit()`|Computed `{ translations, location, branding, gvl, cmpId, customVendors }`, or `undefined` before the policy resolves|Read display data from the resolved policy.|
|`useConsentComponent(name)`|Computed slot attributes|Read the `components` option for one component, to reuse it in custom markup.|

`useConsentKernel().events.on()` returns an unsubscribe function. Call it in
`onUnmounted`. For choice and permission events, prefer the `callbacks`
option, which c15t wires once for the whole app.

See [callbacks](/docs/frameworks/nuxt/callbacks) for the `callbacks` option.
