local-cookie-consent
v0.1.3
Published
Local-only React consent gating library with no backend and no network calls.
Maintainers
Readme
local-cookie-consent
A React + TypeScript component library for local-only cookie/script consent gating.
This package is designed for browser-local preference handling only:
- no backend required
- no network calls
- no server-side consent storage
- no generated visitor identifiers
Privacy-minimizing design
What this library stores
A local consent preference object with:
versionupdatedAt- optional
expiresAt - category decisions map
Example:
type ConsentState = {
version: string;
updatedAt: string;
expiresAt?: string;
categories: Record<string, boolean>;
};What this library intentionally does not collect
- IP address
- user agent
- visitor ID
- session ID
- account ID
- geolocation
- fingerprinting attributes
- request headers
- analytics events
Install
npm install local-cookie-consentBasic setup
import {
ConsentProvider,
ConsentBanner,
ConsentPreferencesModal,
ConsentManageButton,
} from "local-cookie-consent";
const consentConfig = {
version: "2026-06-privacy-v1",
storageKey: "site_cookie_consent",
categories: [
{
id: "necessary",
label: "Necessary",
description: "Required for the site to work.",
required: true,
},
{
id: "analytics",
label: "Analytics",
description: "Helps us understand site usage.",
},
{
id: "marketing",
label: "Marketing",
description: "Used for advertising and conversion measurement.",
},
],
};
export function App() {
return (
<ConsentProvider config={consentConfig}>
<ConsentBanner policyLink={<a href="/privacy">Privacy policy</a>} />
<ConsentPreferencesModal />
<ConsentManageButton />
{/* app content */}
</ConsentProvider>
);
}Category configuration
type ConsentCategoryConfig = {
id: string;
label: string;
description: string;
required?: boolean;
defaultValue?: boolean;
};Rules:
required: truecategories are always enabled- optional categories default to
falseunless explicitly configured otherwise - unknown categories are never treated as allowed
useConsent
const {
consent,
hasChoice,
isAllowed,
acceptAll,
rejectAll,
savePreferences,
resetConsent,
openPreferences,
} = useConsent();Consent gating
Component gating
<ConsentGate category="analytics">
<AnalyticsPanel />
</ConsentGate>Script gating
<ConsentScript
category="analytics"
src="https://example.com/analytics.js"
id="analytics-script"
/>Callback gating
useConsentEffect("analytics", () => {
// run only when analytics is allowed
});Behavior:
- optional categories are denied until explicit consent
- stale version and expired consent force re-consent
- reset/withdraw prevents future optional script injection
Storage adapters
The default storage is a first-party cookie:
SameSite=LaxPath=/Secureon HTTPS- configurable max age
- non-HttpOnly (required for frontend reads)
You can provide your own storage adapter or use localStorage:
import { createLocalStorageAdapter } from "local-consent-gate";
const storage = createLocalStorageAdapter("site_cookie_consent");
<ConsentProvider config={consentConfig} storage={storage}>
<App />
</ConsentProvider>;Adapter interface:
interface ConsentStorage {
read(): ConsentState | null;
write(state: ConsentState): void;
clear(): void;
}Styling and theming
Default styles are included and use CSS variables defined in dist/index.css (source: src/styles/consent.css). You can customize look-and-feel by overriding the variables listed below or by providing your own stylesheet.
Common variables you may override:
--cc-bg--cc-panel--cc-primary--cc-text
How to include the default CSS
- Bundlers (Vite, Webpack, CRA, etc.) — import as a side-effect at app entry so the styles are bundled with your app:
// in your app's root entry (e.g. src/main.tsx or app/root.tsx)
import 'local-cookie-consent/styles.css';- Remix (links API) — use the
?urlimport to get the compiled asset URL and return it fromlinks():
// app/root.tsx (Remix)
import consentStyles from 'local-cookie-consent/dist/index.css?url';
export const links = () => [
{ rel: 'stylesheet', href: consentStyles },
];Notes on imports
local-cookie-consent/styles.cssis the recommended simple import for most apps (side-effect import).local-cookie-consent/dist/index.css?urlorlocal-cookie-consent/styles?urlreturns an asset URL which is useful for frameworks that expectlinks()-style return values.- If your bundler complains about package exports during dev (Vite optimizeDeps), add the package to
optimizeDeps.excludeor import the CSS into your app'spublic/folder as a fallback.
No branding is hardcoded — everything uses CSS variables so you can fully control the appearance.
SSR notes
- no direct
window/documentaccess during server render - storage interaction happens in client effects
- optional categories are denied until a valid client-side consent state exists
Withdrawal/reset flow
Use resetConsent() to withdraw and clear saved consent locally.
After reset, banner can be shown again and optional categories remain denied until new choice.
Versioning and re-consent
Re-consent is required when:
config.versionchanges- stored consent is malformed
- stored consent is expired
- required category constraints are invalid
Safe defaults:
- malformed state is ignored
- missing state means optional categories denied
- stale/expired state asks again
- new optional categories default to false
Limitations
- This package does not maintain backend consent records.
- This package does not prove consent for a specific user for backend audit trails.
- Once third-party scripts run, complete cleanup can require page reload or integration-specific teardown.
- This package is intended for local consent gating only.
- Operators are responsible for deciding whether additional records are needed for their legal/compliance context.
Not legal advice
This package and documentation are technical guidance only and not legal advice.
