Skip to main content

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 appName prop (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:

  1. Add the dependency to package.json:
{
"dependencies": {
"@elcto/cookies-consent": "workspace:*"
}
}
  1. Add to next.config.ts:
const nextConfig: NextConfig = {
transpilePackages: ['@elcto/cookies-consent', '@elcto/ui'],
};
  1. Add Tailwind source directive in globals.css:
@source "../../../../shared/cookies-consent/src/**/*.tsx";
  1. 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>
);
}

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

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>
);
}

Essential (Always enabled)​

Required for the website to function. Cannot be disabled.

CookiePurposeDuration
__Secure-*.session-tokenKeeps you signed in securely30 days
__Secure-*.callback-urlRemembers where to redirect after loginSession
__Host-*.csrf-tokenProtects against CSRF attacksSession
__Secure-*.pkce.*Secures OAuth authentication15 minutes
NEXT_LOCALELanguage preference1 year
cf_clearanceCloudflare security1 year
__cf_bmCloudflare bot protection30 minutes
cf_chl_*Cloudflare TurnstileSession
_cfuvidCloudflare rate limitingSession

Functional (Opt-in)​

Enable personalized features and third-party integrations.

CookiePurposeDurationApp
deletion_modal_dismissedUI preferencePermanentID
sentry-scError trackingSessionAll
twitch_*Twitch integrationVariesConsole
VISITOR_INFO1_LIVE, YSCYouTube embeds6 monthsConsole
_shopify_*Shopify integrationVariesConsole
_gh_sess, logged_inGitHub authenticationSessionID
steamLoginSecureSteam authenticationVariesID
kick_*Kick integrationVariesConsole

Statistics (Opt-in)​

Help understand website usage.

CookiePurposeDurationApp
_ga, _ga_*Google Analytics2 yearsAll
_gidGoogle Analytics24 hoursAll

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 appName are shown
  • Items without an app field 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.

{
"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​

KeyTypePurposeApp
elcto_cookie_consentlocalStorageCookie consent preferencesAll
nextauth.messagelocalStorageSession sync across tabsAll
session_audit_*sessionStorageLogin audit trackingAll

Functional Storage​

KeyTypePurposeApp
console_sidebar_collapsedlocalStorageSidebar collapse stateConsole
console_workspacelocalStorageSelected workspaceConsole
id_sidebar_collapsedlocalStorageSidebar collapse stateID
connections_successsessionStorageAccount linking success messageID

Environment Variables​

VariableDescription
NEXT_PUBLIC_POLICIES_URLURL to policies app (for cookie policy link)
NEXT_PUBLIC_MAIN_URLURL to main app (for contact/imprint links)
NEXT_PUBLIC_GA_MEASUREMENT_IDGoogle Analytics 4 Measurement ID
NEXT_PUBLIC_FB_PIXEL_IDFacebook Pixel ID
NEXT_PUBLIC_HOTJAR_SITE_IDHotjar Site ID
NEXT_PUBLIC_PLAUSIBLE_DOMAINPlausible domain
NEXT_PUBLIC_SENTRY_DSNSentry 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​