{"_id":"@bliztek/consent","name":"@bliztek/consent","dist-tags":{"latest":"1.0.0"},"versions":{"1.0.0":{"name":"@bliztek/consent","version":"1.0.0","description":"Headless, zero-dependency cookie consent hook and types for React. GDPR-ready with granular per-category controls.","type":"module","sideEffects":false,"main":"./dist/index.cjs","module":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}}},"scripts":{"build":"tsup src/index.ts --format esm,cjs --dts --clean","dev":"tsup src/index.ts --format esm,cjs --dts --watch","prepublishOnly":"npm run build","test":"cd ../.. && vitest run packages/consent"},"publishConfig":{"access":"public"},"repository":{"type":"git","url":"git+https://github.com/bliztek/consent.git"},"peerDependencies":{"react":"^18.0.0 || ^19.0.0"},"devDependencies":{"@types/react":"19.2.7","tsup":"^8.5.1","typescript":"^5.9.3"},"keywords":["cookie-consent","gdpr","privacy","react","headless","consent-hook","cookie-preferences"],"license":"MIT","gitHead":"b928f11d3b93d446cb520487bb7b9f22a9bdbfc0","_id":"@bliztek/consent@1.0.0","bugs":{"url":"https://github.com/bliztek/consent/issues"},"homepage":"https://github.com/bliztek/consent#readme","_nodeVersion":"25.6.1","_npmVersion":"11.9.0","dist":{"integrity":"sha512-ZtWrl/7ZmtlaoM/WzZ02SInhNq2+seBM48NPZdh5W/s+I4oso35wmPnqdT8/rrWgfgWt5rC+gda0WzWgTrrg3A==","shasum":"46ff2b76979b4d78be9a83bf3f15770903cff6c8","tarball":"https://registry.npmjs.org/@bliztek/consent/-/consent-1.0.0.tgz","fileCount":7,"unpackedSize":22106,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIEdKZYf+VvJ/YMBhEMqUNCvJJhR4DT3ounrd8RppoqOtAiAv47IMw8HMomDy2KRLdcaDvOc09u6zMVO/EY/VLW5sMA=="}]},"_npmUser":{"name":"bliztek","email":"sbrown@bliztek.com"},"directories":{},"maintainers":[{"name":"bliztek","email":"sbrown@bliztek.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/consent_1.0.0_1773390431339_0.7260597703745304"},"_hasShrinkwrap":false}},"time":{"created":"2026-03-13T08:27:11.240Z","1.0.0":"2026-03-13T08:27:11.483Z","modified":"2026-03-13T08:27:11.717Z"},"maintainers":[{"name":"bliztek","email":"sbrown@bliztek.com"}],"description":"Headless, zero-dependency cookie consent hook and types for React. GDPR-ready with granular per-category controls.","homepage":"https://github.com/bliztek/consent#readme","keywords":["cookie-consent","gdpr","privacy","react","headless","consent-hook","cookie-preferences"],"repository":{"type":"git","url":"git+https://github.com/bliztek/consent.git"},"bugs":{"url":"https://github.com/bliztek/consent/issues"},"license":"MIT","readme":"# @bliztek/consent\n\n![npm](https://img.shields.io/npm/v/@bliztek/consent)\n![License](https://img.shields.io/npm/l/@bliztek/consent)\n![Bundle Size](https://img.shields.io/bundlephobia/min/@bliztek/consent)\n\nHeadless, zero-dependency cookie consent hook and types for React. GDPR-ready with granular per-category controls.\n\n---\n\n## Features\n\n- **Zero runtime dependencies** — only `react` as a peer dep\n- **Headless** — bring your own UI, this package handles state and persistence\n- **`useConsent` hook** — localStorage persistence, legacy migration, accept/reject/save\n- **4 cookie categories**: Necessary (locked on), Analytics, Marketing, Functional\n- **Input validation** — `isConsentPreferences()` type guard validates localStorage data shape, rejects malformed values\n- **TypeScript-first** — full type exports for `ConsentPreferences`, `ConsentCategory`, etc.\n- **SSR-safe** — reads localStorage in `useEffect`, no hydration mismatches\n- **ESM + CJS** — dual module build with proper `exports` field and tree-shaking support\n\n---\n\n## Install\n\n```bash\nnpm install @bliztek/consent\n```\n\nPeer dependency:\n\n```bash\nnpm install react\n```\n\n---\n\n## Quick Start\n\n```tsx\nimport { useConsent } from \"@bliztek/consent\";\n\nfunction App() {\n  const {\n    preferences,\n    acceptAll,\n    rejectAll,\n    setPreferences,\n    showPreferencesPanel,\n    setShowPreferencesPanel,\n    initialized,\n  } = useConsent();\n\n  // Don't render until localStorage has been read\n  if (!initialized) return null;\n\n  // No stored preferences — show a consent banner\n  if (!preferences) {\n    return (\n      <div role=\"alertdialog\" aria-label=\"Cookie consent\">\n        <p>We use cookies to improve your experience.</p>\n        <button onClick={acceptAll}>Accept all</button>\n        <button onClick={rejectAll}>Reject all</button>\n        <button onClick={() => setShowPreferencesPanel(true)}>\n          Manage cookies\n        </button>\n      </div>\n    );\n  }\n\n  return <main>{/* your app */}</main>;\n}\n```\n\n---\n\n## API\n\n### `useConsent(options?)`\n\n```ts\nimport { useConsent } from \"@bliztek/consent\";\n```\n\n#### Options\n\n| Option | Type | Default | Description |\n|--------|------|---------|-------------|\n| `storageKey` | `string` | `\"cookiePreferences\"` | localStorage key for persisted preferences |\n| `legacyKey` | `string` | -- | If set, migrates a legacy `\"granted\"`/`\"denied\"` value to the new format |\n\n#### Return value\n\n| Property | Type | Description |\n|----------|------|-------------|\n| `preferences` | `ConsentPreferences \\| null` | Current consent state, or `null` if no choice has been made |\n| `setPreferences` | `(prefs: ConsentPreferences) => void` | Save granular preferences (persists to localStorage, closes preferences panel) |\n| `acceptAll` | `() => void` | Grant all categories (persists to localStorage, closes preferences panel) |\n| `rejectAll` | `() => void` | Deny all optional categories (persists to localStorage, closes preferences panel) |\n| `showPreferencesPanel` | `boolean` | UI state for toggling a preferences modal |\n| `setShowPreferencesPanel` | `(show: boolean) => void` | Toggle the preferences panel state |\n| `initialized` | `boolean` | `true` once localStorage has been read (safe to render) |\n\n### `isConsentPreferences(value)`\n\nType guard that validates whether a value conforms to the `ConsentPreferences` shape. Used internally to reject malformed localStorage data. Also exported for consumer use.\n\n```ts\nimport { isConsentPreferences } from \"@bliztek/consent\";\n\nconst raw = JSON.parse(localStorage.getItem(\"myKey\") ?? \"null\");\nif (isConsentPreferences(raw)) {\n  // raw is ConsentPreferences\n}\n```\n\n---\n\n## Types\n\n```ts\ntype ConsentCategory = \"necessary\" | \"analytics\" | \"marketing\" | \"functional\";\n\ntype ConsentPreferences = {\n  necessary: true;      // always true, not toggleable\n  analytics: boolean;\n  marketing: boolean;\n  functional: boolean;\n};\n```\n\n### Constants\n\n| Constant | Description |\n|----------|-------------|\n| `CONSENT_CATEGORIES` | Array of `{ key, label, description, locked }` for all 4 categories |\n| `ALL_GRANTED` | All categories set to `true` |\n| `ALL_DENIED` | Only `necessary: true`, rest `false` |\n\n---\n\n## Error Handling\n\nThis library is designed to be resilient in hostile browser environments:\n\n- **localStorage unavailable** (SSR, private browsing, storage disabled): All read/write operations are wrapped in try/catch. The hook initializes with `preferences: null` and `initialized: true`, so your UI renders correctly.\n- **localStorage quota exceeded**: Persistence silently fails, but in-memory state still updates. The user's session works normally; preferences just won't survive a page reload.\n- **Malformed localStorage data**: If another script writes garbage to the storage key, `isConsentPreferences()` rejects it and the hook returns `null` — prompting the user to make a fresh choice.\n\nNo errors are thrown to the consumer. All failure modes result in `preferences: null`.\n\n---\n\n## Integrating with Google Analytics\n\n```tsx\nimport { useConsent } from \"@bliztek/consent\";\n\nfunction AnalyticsLoader() {\n  const { preferences } = useConsent();\n\n  if (!preferences?.analytics) return null;\n\n  return <script src=\"https://www.googletagmanager.com/gtag/js?id=G-XXXXX\" />;\n}\n```\n\n---\n\n## Legacy Migration\n\nIf you previously stored consent as a single `\"granted\"` / `\"denied\"` string, pass the old key to `legacyKey`:\n\n```ts\nconst consent = useConsent({\n  legacyKey: \"userConsent\", // old key — will be read, migrated, and removed\n});\n```\n\nOn first load, the hook will:\n\n1. Check `localStorage` for the new `storageKey` (default `\"cookiePreferences\"`)\n2. If not found, check `legacyKey`\n3. Map `\"granted\"` to `ALL_GRANTED`, `\"denied\"` to `ALL_DENIED`\n4. Write the migrated value to the new key and remove the legacy key\n\n---\n\n## Contributing\n\nContributions are welcome! If you'd like to improve this package:\n\n1. Fork the repository.\n2. Create a new branch:\n   ```bash\n   git checkout -b feature-name\n   ```\n3. Make your changes and commit:\n   ```bash\n   git commit -m \"Add new feature\"\n   ```\n4. Push your branch and open a Pull Request.\n\n---\n\n## More Information\n\nThis package is built and maintained by [Bliztek](https://bliztek.com). We design and develop customized websites and web applications to help businesses of all sizes achieve their goals. Our solutions prioritize scalability, security, and performance to drive growth and ensure long-term success.\n\n- Follow [@Dev4TheWeb](https://x.com/Dev4TheWeb) on Twitter/X for updates from the creator\n- Follow [@Bliztek](https://x.com/bliztek) on Twitter/X for updates from the company side\n- Read our [Company blog](https://bliztek.com/blog) to learn more about our contributions to open source!\n\n---\n\n## License\n\nThis package is licensed under the [MIT License](./LICENSE).\n","readmeFilename":"README.md","_rev":"1-c1549da326f02f7008045d256d1bc3dd"}