SvelteKit Components
ConsentGate
Gate an embed behind consent
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. Save it as its own component, for example
in src/lib, and render it from any route:
ConsentDialog must be mounted in the root layout so the placeholder's link
has a dialog to open. Embeds covers iframes you cannot wrap.
The gate on the server
ConsentGate renders an empty wrapper on the server, even when the visitor's
stored choice allows the category. The embed and the placeholder both appear
in the browser one frame after hydration. So an embed never reaches a cached
or prerendered page, and a visitor who has not allowed it never requests it.
Size the wrapper with class to avoid a layout shift.
Props
| Prop | Type | Default | Behavior |
|---|---|---|---|
category | AllConsentNames | required | The category whose permission the content needs, such as 'measurement' or 'marketing'. |
children | Snippet | none | The gated content. It is not in the DOM until the category is allowed. |
placeholder | Snippet | built-in placeholder | Shown while the category is denied. |
noStyle | boolean | provider's noStyle | Drops c15t's classes from the built-in placeholder. |
class | string | none | Class 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:
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.