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>
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 "Cookie settings" footer link to this (as this site's footer does). |
| string PolicyVersion | Policy version to ask consent for. Set from the banner's PolicyVersion parameter; a stored decision from another version re-prompts. |
| DateTimeOffset? DecidedAtUtc | UTC timestamp of the last recorded decision (proof of consent), or null if none stored. |
| string? DecisionPolicyVersion | Policy version the last recorded decision was given against. Null for a legacy (versionless) record. |
| 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. |