---
title: Components
description: Every c15t component in Nuxt, which ones the Nuxt module registers
  globally, which you import, and a page for each one's props and behavior.
group: frameworks
lastModified: "2026-10-10T16:01:45+01:00"
---
## Component reference

Every component reads the consent runtime the Nuxt module creates, and each
imports its own styles. The module registers the components marked Global, so
you use them in templates without an import. Import the others by path.

|Component|In Nuxt|What it renders|
|--|--|--|
|[`ConsentRoot`](/docs/frameworks/nuxt/components/consent-root)|Global|The banner, rendered on the server, and the preference dialog once opened. Switches to the IAB surfaces under an IAB policy when `iab` is set.|
|[`ConsentWidget`](/docs/frameworks/nuxt/components/consent-widget)|Global|Category switches and save actions inline, for a privacy page.|
|[`ConsentDialogLink`](/docs/frameworks/nuxt/components/consent-dialog-link)|Global|A button that opens preferences.|
|[`ConsentDialogTrigger`](/docs/frameworks/nuxt/components/consent-dialog-trigger)|Global|A floating, draggable button that opens preferences.|
|[`ConsentGate`](/docs/frameworks/nuxt/components/consent-gate)|Global|Its default slot while a category is allowed, otherwise its `placeholder` slot.|
|[`ConsentBanner`](/docs/frameworks/nuxt/components/consent-banner)|`c15t/vue/runtime/components/consent-banner.vue`|The first-layer banner on its own.|
|[`ConsentManager`](/docs/frameworks/nuxt/components/consent-manager)|`c15t/vue/runtime/components/consent-manager.vue`|The preference dialog on its own.|
|[`IABConsentBanner`](/docs/frameworks/nuxt/components/iab-consent-banner)|`c15t/vue/runtime/components/iab-consent-banner.vue`|The IAB TCF first layer.|
|[`IABConsentDialog`](/docs/frameworks/nuxt/components/iab-consent-dialog)|`c15t/vue/runtime/components/iab-consent-dialog.vue`|The IAB TCF preference centre.|
|[`ConsentDevTools`](/docs/frameworks/nuxt/components/dev-tools)|`c15t/vue/devtools`|A development panel for the consent runtime.|

Import the path components inside a Nuxt component's `<script setup>`:

```ts
import ConsentBanner from 'c15t/vue/runtime/components/consent-banner.vue';
```

## ConsentRoot or the parts

`ConsentRoot` renders `ConsentBanner` and `ConsentManager`, or the IAB
components when `iab` is set, for the visitor's policy. Mount `ConsentRoot`, or mount the parts,
never both, or the visitor sees two banners. Always use the global
`ConsentRoot` in Nuxt, not the one `c15t/vue/vue-plugin` exports. The global
one renders the banner on the server. The module writes theme tokens into the page head
whether you mount `ConsentRoot` or the parts.

## Use the components in Vue Vapor

Nuxt 4.6 and later can compile your components with Vue Vapor. Vapor needs
Vue 3.6, which is a release candidate, so install `vue` 3.6 alongside Nuxt and
turn on `vue.vapor`:

```ts title="nuxt.config.ts"
export default defineNuxtConfig({
	c15t: { backendURL: 'https://your-project.inth.app' },
	modules: ['c15t/vue'],
	// Vapor needs Vue 3.6. Opt components in with `<script setup vapor>`.
	vue: { vapor: true },
});
```

Mark a page or component with `<script setup vapor>` to compile it with Vapor.
c15t's components stay on the virtual DOM, and Nuxt renders them inside Vapor
components through its interop layer. Mount `ConsentRoot` in a Vapor
`app.vue`, pass slots to `ConsentGate` or `ConsentDialogLink`, and call
the composables in a Vapor `<script setup>`, as you would elsewhere:

```vue title="app/components/VideoEmbed.vue"
<!-- A Vapor component passing both slots to ConsentGate. -->
<script setup vapor lang="ts"></script>

<template>
	<ConsentGate category="measurement">
		<iframe
			src="https://www.youtube-nocookie.com/embed/czTksCF6X8Y"
			title="YouTube video"
			loading="lazy"
			allowfullscreen
		/>
		<template #placeholder>
			<p>Allow measurement to load this YouTube video.</p>
			<ConsentDialogLink>Choose video permissions</ConsentDialogLink>
		</template>
	</ConsentGate>
</template>
```

Vapor does not make the banner or the preference dialog faster, because they
still render with the virtual DOM. The interop layer ships the Vapor runtime
next to the virtual DOM one, so expect more JavaScript, not less: the
`/consent-example` page of `internals/fixtures/nuxt-vapor` downloads about 18%
more JavaScript than the same app without Vapor.

## Building blocks

The stock surfaces are built from smaller components that the package also
exports under `c15t/vue/runtime/components/`: `consent-button.vue`,
`consent-switch.vue`, `consent-actions.vue`, `consent-link.vue`,
`consent-legal-links.vue`, `consent-description.vue`, `consent-tag.vue` and
`consent-branding-icon.vue`. For consent UI in your own markup, use the
[headless composables](/docs/frameworks/nuxt/headless) instead. They give you
the policy's actions and the draft state without the stock markup.
