Skip to main content

Svelte Components

ConsentGate

ConsentGate mounts its children only while one category is allowed, and shows a placeholder otherwise. Wrap any iframe or widget that sets cookies or contacts a vendor when it loads:

src/YouTubeEmbed.svelte
<script lang="ts">
	import { ConsentDialogLink, ConsentGate } from '@c15t/svelte';
</script>

<!-- The iframe mounts only while measurement is allowed. -->
<ConsentGate category="measurement">
	{#snippet placeholder()}<div class="placeholder">
			<p>Allow measurement to load this YouTube video.</p>
			<ConsentDialogLink>Choose video permissions</ConsentDialogLink>
		</div>{/snippet}
	<iframe
		title="YouTube video"
		src="https://www.youtube-nocookie.com/embed/czTksCF6X8Y?playsinline=1"
		allow="encrypted-media; picture-in-picture"
		allowfullscreen
	></iframe>
</ConsentGate>

Render the component inside ConsentProvider, with ConsentDialog mounted so the placeholder's link has a dialog to open. Embeds covers iframes you cannot wrap, such as those from a CMS.

Props

PropTypeDefaultBehavior
categoryAllConsentNamesrequiredThe category whose permission the content needs, such as 'measurement' or 'marketing'.
childrenSnippetnoneThe gated content. It is not in the DOM until the category is allowed.
placeholderSnippetbuilt-in placeholderShown while the category is denied.
noStylebooleanprovider's noStyleDrops c15t's classes from the built-in placeholder.
classstringnoneClass on the wrapper <div> that stays in the page in every state.

Behavior

ConsentGate renders a <div> wrapper. Inside it, it renders the children while the visitor's effective permission for category is granted, and the placeholder otherwise. The permission is the same value as getConsentManager().has(category). It can be granted before the visitor chooses, under an opt-out policy, and a recorded grant can be overridden by a privacy signal such as Global Privacy Control.

  • While the category is denied, the children are absent from the DOM, so an iframe inside sends no request.
  • When the visitor allows the category, the children mount. No reload.
  • When the visitor withdraws it, the children unmount and the embed stops.
  • Until the component has mounted in the browser, the wrapper is empty. The server HTML never contains the children or the placeholder, and the browser fills the wrapper one frame after mount.

The built-in placeholder shows Accept {category} consent to view this content. and a button labelled Enable {category} consent, with the category's translated title in place of {category}. The copy comes from the consentGate.title and consentGate.actionButton translations. The button opens the preference dialog; it does not grant the category, so mount ConsentDialog in the same provider.

ConsentGate does not add its category to the preference dialog. Keep the category in your policy's scope, and in consentCategories if you set that prop.

Accessibility

The built-in placeholder is plain text and a <button>, so it works without the embed. Give the iframe a descriptive title, and give visitors who decline another way to the content, such as a link to the video's page or a store address next to the map. A category the policy blocks cannot be allowed from preferences.

Style the placeholder

Set class on ConsentGate to size the wrapper to the embed, for example an aspect-ratio for a video, so the page does not shift when the embed replaces the placeholder.

To restyle the built-in placeholder, set the consentGate slot for the card, consentGateTitle and consentGateButton in the provider's theme.slots. consentGateButton applies on top of buttonPrimary:

const theme = {
	slots: {
		consentGate: 'brand-gate',
		consentGateButton: { className: 'brand-gate-button' },
	},
};

The built-in placeholder carries data-testid="consent-gate-placeholder", its title data-testid="consent-gate-title" and its button data-testid="consent-gate-button". For a different design, pass your own placeholder snippet.