Cookie Consent Overview
The @elcto/cookies-consent package provides a GDPR-compliant cookie consent banner and management system for Heimdall platform applications.
Features
- GDPR-compliant cookie consent modal
- 4 cookie categories: Essential, Functional, Statistics, Marketing
- Customizable consent preferences
- Floating cookie settings button for non-logged-in users
- Cookie detection and categorization
- Browser storage detection (localStorage & sessionStorage)
- App-specific filtering for cookies and storage via
appNameprop (for multi-domain setups) - Multi-language support (English & German)
- Persistent consent storage in localStorage
Installation
The library is already included in platform/id, platform/backend, and platform/policies. For new Next.js apps in the monorepo:
- Add the dependency to
package.json:
{
"dependencies": {
"@elcto/cookies-consent": "workspace:*"
}
}
- Add to
next.config.ts:
const nextConfig: NextConfig = {
transpilePackages: ['@elcto/cookies-consent', '@elcto/ui'],
};
- Add Tailwind source directive in
globals.css:
@source "../../../../shared/cookies-consent/src/**/*.tsx";
- Run
pnpm install
Quick Start
Basic Setup
Wrap your app with the ConsentProvider:
import { ConsentProvider, defaultTranslations } from "@elcto/cookies-consent";
export default function RootLayout({ children }: { children: React.ReactNode }) {
const locale = "en"; // or get from your i18n setup
return (
<ConsentProvider
translations={defaultTranslations[locale]}
policyUrl={`/${locale}/cookies`}
>
{children}
</ConsentProvider>
);
}
With Footer Links
Add links to Privacy, Contact, and Imprint pages:
<ConsentProvider
translations={defaultTranslations[locale]}
policyUrl={`/${locale}/cookies`}
footerLinks={{
privacyUrl: `/${locale}/privacy`,
contactUrl: "/contact",
imprintUrl: "/imprint",
}}
>
{children}
</ConsentProvider>
With App-Specific Filtering (Multi-Domain Setups)
When running apps on different top-level domains, use appName to filter cookies and storage items to only show those relevant to the current app:
<ConsentProvider
translations={defaultTranslations[locale]}
policyUrl={`/${locale}/cookies`}
appName="Console" // Only shows cookies/storage for "Console" or "All"
>
{children}
</ConsentProvider>
Available app names: "Console", "ID", "Policies". Items with app: "All" (or "Alle" in German) are shown in all apps.
This is useful when:
- Apps run on separate domains (not subdomains)
- Each app has different third-party integrations
- You want the consent modal to only show relevant items
Accessing Consent State
Use the useConsent hook to check consent and open preferences:
import { useConsent } from "@elcto/cookies-consent";
function MyComponent() {
const { hasConsented, isAllowed, openPreferences } = useConsent();
// Check if user has made a choice
if (!hasConsented) {
return <p>Please accept or reject cookies</p>;
}
// Check specific category
if (isAllowed("statistics")) {
// Load analytics
}
return (
<button onClick={openPreferences}>
Cookie Settings
</button>
);
}
Cookie Categories
Essential (Always enabled)
Required for the website to function. Cannot be disabled.
| Cookie | Purpose | Duration |
|---|---|---|
__Secure-*.session-token | Keeps you signed in securely | 30 days |
__Secure-*.callback-url | Remembers where to redirect after login | Session |
__Host-*.csrf-token | Protects against CSRF attacks | Session |
__Secure-*.pkce.* | Secures OAuth authentication | 15 minutes |
NEXT_LOCALE | Language preference | 1 year |
cf_clearance | Cloudflare security | 1 year |
__cf_bm | Cloudflare bot protection | 30 minutes |
cf_chl_* | Cloudflare Turnstile | Session |
_cfuvid | Cloudflare rate limiting | Session |
Functional (Opt-in)
Enable personalized features and third-party integrations.
| Cookie | Purpose | Duration | App |
|---|---|---|---|
deletion_modal_dismissed | UI preference | Permanent | ID |
sentry-sc | Error tracking | Session | All |
twitch_* | Twitch integration | Varies | Console |
VISITOR_INFO1_LIVE, YSC | YouTube embeds | 6 months | Console |
_shopify_* | Shopify integration | Varies | Console |
_gh_sess, logged_in | GitHub authentication | Session | ID |
steamLoginSecure | Steam authentication | Varies | ID |
kick_* | Kick integration | Varies | Console |
Statistics (Opt-in)
Help understand website usage.
| Cookie | Purpose | Duration | App |
|---|---|---|---|
_ga, _ga_* | Google Analytics | 2 years | All |
_gid | Google Analytics | 24 hours | All |
Marketing (Opt-in)
Used for advertising (currently not in use).
App-Specific Filtering
Both cookies and browser storage items support an optional app field that specifies which app(s) they belong to. When the appName prop is set on ConsentProvider, items are filtered:
- Items with
app: "All"(or"Alle"in German) are shown in all apps - Items matching the current
appNameare shown - Items without an
appfield are shown in all apps - Items with an array like
app: ["Console", "ID"]are shown in those specific apps
This is particularly useful for multi-domain setups where different apps use different third-party services.
Example: Multi-App Cookie
{
"name": "shared_preference",
"purpose": "Shared user preference",
"duration": "1 year",
"app": ["Console", "ID"]
}
This cookie will show in Console and ID apps, but not in Policies.
Browser Storage
The consent modal also displays localStorage and sessionStorage items for transparency.
Essential Storage
| Key | Type | Purpose | App |
|---|---|---|---|
elcto_cookie_consent | localStorage | Cookie consent preferences | All |
nextauth.message | localStorage | Session sync across tabs | All |
session_audit_* | sessionStorage | Login audit tracking | All |
Functional Storage
| Key | Type | Purpose | App |
|---|---|---|---|
console_sidebar_collapsed | localStorage | Sidebar collapse state | Console |
console_workspace | localStorage | Selected workspace | Console |
id_sidebar_collapsed | localStorage | Sidebar collapse state | ID |
connections_success | sessionStorage | Account linking success message | ID |
Environment Variables
| Variable | Description |
|---|---|
NEXT_PUBLIC_POLICIES_URL | URL to policies app (for cookie policy link) |
NEXT_PUBLIC_MAIN_URL | URL to main app (for contact/imprint links) |
NEXT_PUBLIC_GA_MEASUREMENT_ID | Google Analytics 4 Measurement ID |
NEXT_PUBLIC_FB_PIXEL_ID | Facebook Pixel ID |
NEXT_PUBLIC_HOTJAR_SITE_ID | Hotjar Site ID |
NEXT_PUBLIC_PLAUSIBLE_DOMAIN | Plausible domain |
NEXT_PUBLIC_SENTRY_DSN | Sentry DSN (respects functional consent) |
Package Structure
shared/cookies-consent/
├── src/
│ ├── index.ts # Main exports
│ ├── components/
│ │ ├── ConsentBanner/ # Modal component
│ │ ├── CookieSettingsButton/ # Floating button
│ │ ├── CookieStatusIndicator/ # Status indicator
│ │ ├── ConditionalScript/ # Conditional script loading
│ │ └── Analytics/ # Pre-built analytics integrations
│ ├── context/
│ │ └── ConsentContext.tsx # Provider & hooks
│ ├── hooks/
│ │ ├── useConsent.ts # Main consent hook
│ │ ├── useCookiePreferences.ts # Preferences hook
│ │ ├── useCookieDetector.ts # Cookie detection hook
│ │ ├── useCookieStatus.ts # Combined status hook
│ │ ├── useStorageDetector.ts # Browser storage detection
│ │ └── useBrowserDataDetector.ts # Combined cookie + storage detection
│ ├── types/
│ │ └── consent.ts # Type definitions
│ ├── utils/
│ │ ├── storage.ts # localStorage utilities
│ │ ├── cookieDetector.ts # Cookie detection
│ │ └── storageDetector.ts # Browser storage detection
│ └── i18n/
│ ├── en.json # English translations
│ └── de.json # German translations
Next Steps
- Components - ConsentBanner & CookieSettingsButton
- Hooks - useConsent & useCookiePreferences
- Types - Type definitions
- Translations - i18n customization