cookie-consent/README.md
Pdn Technology 9a001197e1
Some checks are pending
CI / test (20) (push) Waiting to run
CI / test (22) (push) Waiting to run
Tema ayarları eklendi
2026-06-20 08:58:11 +03:00

485 lines
13 KiB
Markdown

# @pdntechnology/cookie-consent
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
[![npm version](https://img.shields.io/npm/v/@pdntechnology/cookie-consent.svg)](https://www.npmjs.com/package/@pdntechnology/cookie-consent)
A lightweight, open-source **Consent Management Platform (CMP)** for React and Next.js that implements [Google Consent Mode v2](https://developers.google.com/tag-platform/security/guides/consent) the same way certified tools do.
No heavy dependencies. No vendor lock-in. Works with Google Tag Manager, GA4, Google Ads, and Meta Pixel.
---
## Why this exists
Google-certified CMPs (Cookiebot, OneTrust, CookieYes, …) follow the same technical flow:
1. Set **denied** defaults **before** any Google tag loads
2. Load GTM / gtag.js
3. Show a consent banner
4. On user choice → `gtag('consent', 'update', …)` + dataLayer event
5. When ads are denied → enable `ads_data_redaction` and `url_passthrough`
This library implements that flow in ~15 KB of readable JavaScript.
> **Note:** Certified CMPs additionally provide IAB TCF v2.2 signals and appear on Google's certified vendor list. This library covers the **Consent Mode v2 technical integration**. You are responsible for privacy policy text and legal compliance.
---
## Features
- Google Consent Mode v2 (all 7 signals)
- Granular categories: Necessary / Analytics / Marketing
- Customizable banner text, theme colors, and cookie inventory
- `ads_data_redaction` + `url_passthrough` in bootstrap and on update
- Regional default consent (EEA, etc.)
- React banner + Next.js bootstrap component
- Optional GTM and Meta Pixel loaders (consent-gated)
- Framework-agnostic core API (vanilla JS supported)
- TypeScript definitions for core API and React components
- Error callbacks when consent cannot be saved
> Turkish documentation: [README.tr.md](README.tr.md)
---
## Installation
```bash
npm install @pdntechnology/cookie-consent
```
### Next.js setup
Add to `next.config.mjs`:
```js
const nextConfig = {
transpilePackages: ["@pdntechnology/cookie-consent"],
};
```
---
## Quick start
### 1. Configure (optional)
```js
// lib/consent-config.js
import { configureConsent } from "@pdntechnology/cookie-consent";
configureConsent({
storageKey: "my_site_consent", // localStorage key
waitForUpdateMs: 500, // time GTM waits for consent update
adsDataRedaction: true, // Google recommendation when ads denied
urlPassthrough: true, // pass click IDs in URL when cookies denied
debug: false,
});
```
Import this file in your root layout **before** `ConsentBootstrap`.
### 2. Bootstrap consent defaults (before GTM)
```jsx
// app/layout.jsx
import "@pdntechnology/cookie-consent/styles.css";
import "@/lib/consent-config";
import { ConsentBootstrap } from "@pdntechnology/cookie-consent/react";
export default function RootLayout({ children }) {
return (
<html>
<body>
<ConsentBootstrap />
{children}
</body>
</html>
);
}
```
### 3. Banner + tags
```jsx
// app/providers.jsx
import {
CookieConsent,
GoogleTagManager,
FacebookPixel,
} from "@pdntechnology/cookie-consent/react";
export function Providers({ children }) {
return (
<>
{children}
<CookieConsent
policyHref="/privacy"
labels={{
accept: "Accept all",
reject: "Reject all",
manage: "Manage preferences",
}}
/>
<GoogleTagManager gtmId="GTM-XXXXXXX" />
<FacebookPixel pixelId="123456789" />
</>
);
}
```
### 4. Re-open preferences (footer link)
```jsx
import { openPreferences } from "@pdntechnology/cookie-consent";
<button type="button" onClick={openPreferences}>
Cookie settings
</button>
```
---
## Customization
### Text and policy URL
Every visible string can be overridden via the `labels` prop. The policy link uses `policyHref`.
```jsx
<CookieConsent
policyHref="/privacy"
labels={{
title: "Cookie preferences",
message: "We use cookies to improve your experience.",
policyLink: "Privacy policy",
accept: "Accept all",
reject: "Reject all",
manage: "Manage preferences",
save: "Save preferences",
necessaryTitle: "Necessary",
necessaryDescription: "Required for core site functionality.",
analyticsTitle: "Analytics",
analyticsDescription: "Google Analytics (_ga, _gid).",
marketingTitle: "Marketing",
marketingDescription: "Google Ads and Meta Pixel.",
alwaysOn: "Always on",
storageError: "Your preferences could not be saved. Please try again.",
}}
/>
```
| Label key | Default |
|-----------|---------|
| `title` | Cookie preferences |
| `message` | We use cookies to improve your experience… |
| `policyLink` | Privacy policy |
| `accept` | Accept all |
| `reject` | Reject all |
| `manage` | Manage preferences |
| `save` | Save preferences |
| `necessaryTitle` | Necessary |
| `necessaryDescription` | Required for core site functionality… |
| `analyticsTitle` | Analytics |
| `analyticsDescription` | Helps us understand how visitors use the site… |
| `marketingTitle` | Marketing |
| `marketingDescription` | Used for ads and remarketing… |
| `alwaysOn` | Always on |
| `cookiesHeading` | View cookies |
| `cookieName` | Name |
| `cookiePurpose` | Purpose |
| `cookieDuration` | Duration |
| `cookieProvider` | Provider |
| `storageError` | Your preferences could not be saved… |
### Theme colors
Pass a `theme` object to override CSS variables without writing custom CSS:
```jsx
<CookieConsent
theme={{
background: "rgba(15, 23, 42, 0.97)",
text: "#f8fafc",
textMuted: "rgba(248, 250, 252, 0.82)",
link: "#93c5fd",
primary: "#2563eb",
primaryText: "#ffffff",
secondaryBackground: "rgba(255, 255, 255, 0.1)",
border: "rgba(255, 255, 255, 0.14)",
accent: "#2563eb",
}}
/>
```
You can also pass `className` for additional styling hooks.
### Cookie inventory table
Provide a `cookies` array to show an expandable table inside each category in the preferences panel:
```jsx
<CookieConsent
cookies={[
{
category: "necessary",
name: "my_site_consent",
purpose: "Stores your consent choice",
duration: "1 year",
provider: "This site",
},
{
category: "analytics",
name: "_ga",
purpose: "Visitor statistics",
duration: "2 years",
provider: "Google Analytics",
},
]}
/>
```
If `cookies` is omitted, the banner keeps the simpler three-category view.
### Error handling
Consent write functions return a result object:
```js
const result = acceptAll();
if (!result.ok) {
console.error(result.reason); // "invalid" | "storage"
}
```
In React, use callbacks on the banner:
```jsx
<CookieConsent
onConsentSaved={(consent) => console.log("saved", consent)}
onConsentError={({ reason }) => console.warn("failed", reason)}
/>
```
Enable `debug: true` in `configureConsent()` to log warnings to the console, or use `onStorageError` for global handling.
---
## Example app
See [`examples/next-app`](examples/next-app) for a working Next.js demo with Turkish labels, custom theme colors, and a cookie inventory table.
```bash
cd examples/next-app
npm install
npm run dev
```
---
| Banner category | User choice | Google Consent Mode signals |
|-----------------|-------------|----------------------------|
| **Necessary** | Always on | `security_storage: granted` |
| **Analytics** | Opt-in | `analytics_storage` |
| **Marketing** | Opt-in | `ad_storage`, `ad_user_data`, `ad_personalization`, `functionality_storage`, `personalization_storage` |
When marketing is **denied**, the library also sets:
- `ads_data_redaction: true`
- `url_passthrough: true`
---
## Project structure
```
src/
├── index.js Public API
├── config.js configureConsent()
├── constants.js Events, signal names
├── preferences.js Parse / serialize user choices
├── storage.js localStorage read/write
├── consent-api.js acceptAll, rejectAll, getConsent, …
├── cookie-schema.js Cookie inventory helpers
├── google/
│ ├── gtag.js dataLayer helpers
│ ├── consent-signals.js Category → signal mapping
│ ├── consent-lifecycle.js default + update calls
│ └── bootstrap-script.js Inline <head> script generator
└── react/
├── ConsentBootstrap.jsx
├── CookieConsent.jsx
├── CookieList.jsx
├── theme.js
├── GoogleTagManager.jsx
└── FacebookPixel.jsx
```
---
## API reference
### Configuration
| Function | Description |
|----------|-------------|
| `configureConsent(config)` | Set storage key, wait time, region defaults |
| `getConsentConfig()` | Read active configuration |
### User consent
| Function | Description |
|----------|-------------|
| `getConsent()` | Read stored preferences |
| `setConsent({ analytics, marketing })` | Save custom choice; returns `{ ok, consent \| reason }` |
| `acceptAll()` | Grant all categories; returns `{ ok, consent \| reason }` |
| `rejectAll()` | Deny optional categories; returns `{ ok, consent \| reason }` |
| `hasAnalyticsConsent()` | Check analytics opt-in |
| `hasMarketingConsent()` | Check marketing opt-in |
| `openPreferences()` | Re-open banner panel |
| `syncConsentFromStorage()` | Re-apply stored consent after GTM loads |
### Google Consent Mode
| Function | Description |
|----------|-------------|
| `getConsentBootstrapScript()` | Inline script for vanilla HTML |
| `applyConsentDefaults()` | Set denied defaults programmatically |
| `applyConsentUpdate(consent)` | Push user choice to gtag |
| `buildGoogleConsentState(consent)` | Get signal object |
### Events
| Constant | Fired when |
|----------|------------|
| `EVENT_CONSENT_UPDATED` | User saves a choice |
| `EVENT_OPEN_PREFERENCES` | Preferences panel should open |
| `GTM_CONSENT_EVENT` | Pushed to dataLayer (`cookie_consent_update`) |
### `CookieConsent` props
| Prop | Type | Description |
|------|------|-------------|
| `labels` | `object` | Override banner text |
| `theme` | `object` | Override banner colors |
| `cookies` | `CookieEntry[]` | Optional cookie inventory table |
| `policyHref` | `string` | Privacy / cookie policy URL |
| `LinkComponent` | `component` | Custom link component (default: Next.js `Link`) |
| `className` | `string` | Extra CSS class on the banner root |
| `onConsentSaved` | `(consent) => void` | Called after a successful save |
| `onConsentError` | `({ reason }) => void` | Called when save fails |
---
## Vanilla HTML (no React)
```html
<head>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('consent', 'default', {
ad_storage: 'denied',
analytics_storage: 'denied',
ad_user_data: 'denied',
ad_personalization: 'denied',
functionality_storage: 'denied',
personalization_storage: 'denied',
security_storage: 'granted',
wait_for_update: 500
});
</script>
<!-- GTM snippet here -->
</head>
```
Or generate the script programmatically:
```js
import { getConsentBootstrapScript } from "@pdntechnology/cookie-consent";
console.log(getConsentBootstrapScript());
```
---
## GTM integration checklist
1. Enable **Consent Mode** in your GTM container settings
2. Configure GA4 / Google Ads tags to require appropriate consent signals
3. Optional: create a trigger on custom event `cookie_consent_update`
4. Test with [Tag Assistant](https://tagassistant.google.com/)
---
## Regional defaults (EEA example)
```js
configureConsent({
regionDefaults: [
{
regions: ["AT", "BE", "BG", "HR", "CY", "CZ", "DK", "EE", "FI", "FR",
"DE", "GR", "HU", "IE", "IT", "LV", "LT", "LU", "MT", "NL",
"PL", "PT", "RO", "SK", "SI", "ES", "SE", "IS", "LI", "NO"],
ad_storage: "denied",
analytics_storage: "denied",
ad_user_data: "denied",
ad_personalization: "denied",
},
],
});
```
---
## Troubleshooting
| Problem | Likely cause | Fix |
|---------|--------------|-----|
| Banner never appears | Consent already stored | Clear `localStorage` key or call `openPreferences()` |
| GTM fires before consent | Bootstrap missing or loaded too late | Add `<ConsentBootstrap />` with `beforeInteractive` before GTM |
| Consent not saved | Storage blocked (private mode, Safari ITP) | Handle `onConsentError`; show inline error from banner |
| Tags still fire after reject | GTM tags not using Consent Mode | Enable Consent Mode in GTM and set tag consent requirements |
| TypeScript errors on `./react` | Missing types path | Import from `@pdntechnology/cookie-consent/react` (types included since v1.1) |
---
```bash
# In this repo
cd cookie-consent
npm link
# In your Next.js app
npm link @pdntechnology/cookie-consent
```
Or use a file dependency:
```json
{
"dependencies": {
"@pdntechnology/cookie-consent": "file:../cookie-consent"
}
}
```
---
## Publishing to npm
```bash
npm login
npm publish --access public
```
---
## Contributing
See [CONTRIBUTING.md](CONTRIBUTING.md). Bug reports and pull requests are welcome.
---
## License
[MIT](LICENSE) © [PDN Technology](https://pdntechnology.com)