---
title: c15tVue plugin
description: Install c15t in a Vue app with app.use(c15tVue), what the plugin
  starts when the app mounts, and every option it accepts.
group: frameworks
lastModified: "2026-10-10T16:01:45+01:00"
---
## Install the plugin

Import `c15tVue` from `c15t/vue/vue-plugin` and pass it to `app.use()` in
`src/main.ts`:

```ts title="src/main.ts"
import { posthog } from '@c15t/integrations/posthog';
import { c15tVue, manifest } from 'c15t/vue/vue-plugin';
import { createApp } from 'vue';

import App from './App.vue';

createApp(App)
	.use(c15tVue, {
		mode: manifest(),
		scripts: [
			posthog({
				id: 'phc_your_project_key',
				initOptions: { cookieless_mode: 'never' },
				loadMode: 'after-consent',
			}),
		],
	})
	.mount('#app');
```

`c15t/vue` is the Nuxt module, not a Vue plugin. Passing it to `app.use()`
does nothing useful. The [quickstart](/docs/frameworks/vue/quickstart) covers
`consentManifest()`, the Vite plugin that downloads the policy `manifest()`
reads.

## What the plugin does

`app.use(c15tVue, options)` creates one consent runtime for the app and
provides it to every component. It also adds your `tokens` to
`document.head` in a `<style id="c15t-css-vars">` element, before the first
render. Nothing else runs in the browser until the root component mounts.
Then the plugin, in order:

1. Reads the visitor's stored choice from the `c15t` cookie and
   localStorage.
2. Applies Global Privacy Control when the browser sends it.
3. Starts the script loader for `scripts`, clear-on-revocation, the network
   blocker and the iframe blocker.
4. Resolves the visitor's policy with the `mode` you passed: from the
   manifest in the browser with `manifest()`, from the backend's `/init` with
   `hosted()`, or from local rules with `offline()`.
5. With the `iab` option set, loads the IAB TCF module once a policy uses the
   `iab` model.

Until the policy arrives, every optional category is denied, so gated scripts
and iframes wait. On Vue 3.5 and later, `app.unmount()` stops every module
and removes the token style element.
Composables called outside this app throw `[c15t] Kernel not found`.

## Options

`mode` is required unless you pass a `runtime`; every other option is
optional. Options that hold
functions, such as `scripts` and `callbacks`, work here because the plugin
runs in the browser.

### Policy source

|Option|Default|What it does|
|--|--|--|
|`mode`|Required|Where the visitor's policy comes from: `manifest()`, `hosted()`, `offline()` or `custom()`, all from `c15t/vue/vue-plugin`. See [rendering](/docs/frameworks/vue/rendering).|
|`consentCategories`|The policy's categories|Limits the categories the dialog and widget show, within the policy's scope.|
|`vendors`|Unset|Vendors listed under their category with their own switch. See [vendor consent](/docs/frameworks/vue/vendor-consent).|
|`storageConfig`|`{ storageKey: 'c15t' }`|Cookie and localStorage name, `crossSubdomain`, `defaultDomain` and `defaultExpiryDays`, 365 by default.|

The backend URL, a custom `fetch` and the save `domain` are options of the
mode, not the plugin:

|Mode|Options|
|--|--|
|`manifest()`|`backendURL` and the build snapshot default to what `consentManifest()` downloaded. `source: 'runtime'` fetches the manifest when the app starts; `manifestURL` fetches it from that URL instead of using the snapshot; `snapshot` takes one you supply. `geoURL`, `inputs`, `initFallback`, `headers`, `credentials`, `fetch`, `domain`.|
|`hosted()`|`backendURL`, by default the one `consentManifest()` read from `VITE_C15T_BACKEND_URL` or `VITE_INTH_PROJECT_URL`. `headers`, `fetch`, `domain`.|
|`offline()`|`policyRules`. Omitted, c15t's recommended rules.|

`manifest()` and `hosted()` throw when they have no backend URL: add
`consentManifest()` to `vite.config.ts` and set `VITE_C15T_BACKEND_URL` (or
`VITE_INTH_PROJECT_URL`), or pass `backendURL`.

### Scripts and blocking

|Option|Default|What it does|
|--|--|--|
|`scripts`|Unset|Vendor scripts that load by consent category. See [scripts](/docs/frameworks/vue/scripts).|
|`networkBlocker`|Off|Rules that hold `fetch` and XHR requests until their category is allowed. See [network blocker](/docs/frameworks/vue/network-blocker).|
|`iframeBlocker`|On|Gates iframes that carry `data-src` and `data-category`. `false` turns it off. See [embeds](/docs/frameworks/vue/embeds).|
|`nonce`|Unset|Content Security Policy nonce for every script the loader creates and for the token style element. See [Content Security Policy](/docs/frameworks/vue/content-security-policy).|
|`reloadOnConsentRevoked`|`true`|Reloads the page when a save withdraws a granted category or vendor.|
|`clearOnRevocation`|Unset|Deletes first-party cookies and storage keys for a withdrawn category. See [clear on revocation](/docs/frameworks/vue/clear-on-revocation).|
|`iab`|Off|Turns IAB TCF on; `{}` is enough. Also takes publisher settings, such as `publisherRestrictions`; fields left out come from the policy. Unset, `false` or `{ enabled: false }` leaves IAB off, and an `iab` policy with a vendor list throws an `IABUnavailableError`. See [IAB TCF](/docs/frameworks/vue/iab).|

### Events and other tools

|Option|Default|What it does|
|--|--|--|
|`callbacks`|Unset|`onChoiceRecorded`, `onPermissionsChanged`, `onError` and `onBeforeConsentRevocationReload`. See [callbacks](/docs/frameworks/vue/callbacks).|

### Appearance

|Option|Default|What it does|
|--|--|--|
|`tokens`|The default theme|CSS variables the plugin adds to `document.head` in a `<style id="c15t-css-vars">` element. See [customize](/docs/frameworks/vue/customize#set-theme-tokens).|
|`presentation`|Policy defaults|Banner and dialog variant, position, action layout and blocking.|
|`components`|Unset|Attributes, such as `class`, for named parts of each component.|
|`legalLinks`, `bannerLegalLinks`, `dialogLegalLinks`|None shown|Links to your privacy policy and similar pages, and which surfaces show them.|
|`hideBranding`, `bannerHideBranding`, `dialogHideBranding`, `iabBannerHideBranding`, `iabDialogHideBranding`|`false`|Hide the "Secured by" tag.|
|`showTrigger` and the `trigger*` options|`false`|The floating trigger. See [ConsentDialogTrigger](/docs/frameworks/vue/components/consent-dialog-trigger).|
|`disableAnimation`|`false`|Turns off banner and dialog transitions.|
|`models`, `bannerModels`, `iabBannerModels`, `iabDialogModels`|Every model|Limit which policy models show a surface.|

### Advanced

|Option|What it does|
|--|--|
|`runtime`|A runtime created elsewhere with `createConsentRuntime()`, in place of `mode`. The plugin renders it and neither starts nor stops it, and ignores `callbacks`, blockers, `scripts` and `tokens`, which the runtime's owner configures.|
|`prefetch`, `initialRecords`|A resolved policy and stored records from a server, used for the first render. Plain Vue has no server helper that produces them.|

## Verify

Open the app with the Network tab open. `manifest()` with a bundled snapshot
makes no policy request. `manifest({ source: 'runtime' })` requests
`${backendURL}/manifest`, and `hosted()` requests the backend's `/init`. In
the Application panel, a `c15t` cookie appears after you accept or reject.
