Consent Banner
GDPR-ready cookie / tracking consent primitive. Persists per-category choices in localStorage and exposes them via ConsentService for gating third-party scripts.
Installation
dotnet add package Lumeo
One-time app setup (AddLumeo(), CSS & JS) is covered in the
installation guide.
Usage
@using Lumeo <ConsentBanner />
When to use
- Any site that loads analytics (Google Analytics, Cloudflare Web Analytics, Plausible, Umami) in the EU / UK / CA.
- Any site that embeds third-party scripts users should opt into (YouTube embeds, Intercom, Hotjar).
- SaaS apps that want to give end users a "Manage cookie preferences" link in the footer.
No live preview here — the banner can only show once per browser. Copy the code into your own MainLayout.razor to see it slide in.
Drop the banner into your root layout. Default categories are `necessary` (locked) + `analytics`.
Analytics consent:
No beacon. Nothing leaves the browser.
Toggle Analytics in to see this flip.
The whole point: nothing hits a third-party until the user says yes.
Session, theme preference, routing — required for the site to work.
Cloudflare Web Analytics — anonymous aggregate page-view stats.
Meta Pixel and LinkedIn Insight Tag for remarketing.
YouTube, Vimeo, Tweet embeds — set cookies when loaded.
Define your own buckets — one per third-party service you load.
We use a minimal set of cookies to run the site and optionally measure anonymous usage to improve it. You choose what's on.
Every string is a parameter. Pair with `ILumeoLocalizer` if you want them to follow the user's language.
API reference
<ConsentBanner>
| Prop | Type | Default | Description |
|---|---|---|---|
| Categories | IReadOnlyList<ConsentCategory> | [necessary, analytics] | Buckets shown in the preferences dialog. Replace entirely to match your own site's third-party services. |
| PrivacyPolicyUrl | string? | null | Rendered as a link in the banner copy. Omit to hide the link. |
| Title | string | "Cookies & consent" | Heading shown at the top of the banner. |
| Description | string | default blurb | Body copy below the heading. |
| PrivacyPolicyLabel | string | "Read the privacy policy" | Link text for the privacy-policy anchor. |
| AcceptLabel | string | "Accept all" | Primary button label. |
| RejectLabel | string | "Reject optional" | Secondary button label. Rejection means every non-required category is stored as false. |
| CustomizeLabel | string | "Customize" | Opens the preferences dialog with per-category toggles. |
| PreferencesTitle | string | "Consent preferences" | Heading inside the preferences dialog. |
| PreferencesDescription | string | default | Body copy inside the preferences dialog. |
| RequiredBadge | string | "Required" | Badge shown next to locked-on categories. |
| SaveLabel / CancelLabel | string | "Save preferences" / "Cancel" | Preferences dialog footer buttons. |
| AnimationClass | string | "animate-slide-in-from-bottom" | Tailwind animation class used for the banner entry. |
ConsentCategory
| Property | Type | Description |
|---|---|---|
| Key | string | Machine-readable identifier (e.g. "analytics"). Used as the persistence key and the argument to ConsentService.HasConsent(key). |
| Title | string | Short label shown in the preferences dialog. |
| Description | string | One-sentence explanation for the end user. |
| Required | bool | Locked-on toggle. Use for strictly-necessary cookies (session, theme preference). |
ConsentService
| Member | Description |
|---|---|
| bool HasDecided | True once the user has answered the banner. While false, the banner shows. |
| bool HasConsent(string key) | Did the user consent to this category? "necessary" always returns true; unknown keys return false. |
| Task EnsureLoadedAsync() | Hydrate from localStorage. Called automatically by the banner on first render; safe to call yourself before the banner mounts. |
| Task AcceptAllAsync(IEnumerable<string>) | Grant every non-necessary category. Persists to localStorage. Fires OnChange. |
| Task RejectAllAsync(IEnumerable<string>) | Deny every non-necessary category. Persists and fires OnChange. |
| Task SetManyAsync(IReadOnlyDictionary<string, bool>) | Commit a per-category map. Used by the preferences dialog's Save button. |
| Task ResetAsync() | Clear the stored decision — banner re-appears on next render. |
| void RequestOpenPreferences() | Ask any mounted <ConsentBanner> to pop its dialog. Wire a "Manage cookies" footer link to this. |
| event Action? OnChange | Fires when any category value changes. Subscribe if you want to dynamically load / unload scripts. |
| event Action? OnRequestOpenPreferences | Internal coordination between RequestOpenPreferences and the banner. |