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