# useConsent

> A React cookie consent hook: which categories a visitor allowed, when and under which policy version, shared by banner, links and scripts.

- Type: React hook (Hooks & Utilities)
- Access: Pro (the source needs a Wingo UI Pro plan, see https://wingo-ui.com/pricing)
- Version: 1.0.0
- Install: `npx wingo-ui@latest add use-consent`
- Docs: https://wingo-ui.com/components/use-consent

## Interaction

Press Accept all or Reject all, or flip the categories and press Save choices: the record (method, time, choices) and the Google Consent Mode values update, and the video embed loads only once Marketing is allowed. Reset forgets the decision. Raise the policy version in the playground and the stored choice no longer counts, so the site would ask again. The choice survives a reload and, with local storage, follows you to other tabs.

## Installation

```bash
npx wingo-ui@latest add use-consent
```

Pro item: run `npx wingo-ui@latest login` once with a Pro account. The CLI adds the files, their dependencies and a version record for updates.

With shadcn: `npx shadcn@latest add @wingo-ui/use-consent` (needs the @wingo-ui registry with your API key in components.json)

Files:

- `hooks/use-consent.ts`

Wingo UI dependencies (installed automatically):

- [usePersistedState](https://wingo-ui.com/components/use-persisted-state) ([Markdown](https://wingo-ui.com/components/use-persisted-state.md))

## Usage

```tsx
import { useConsent } from "@/hooks/use-consent"

export function YoutubeEmbed({ id }: { id: string }) {
  const { hydrated, has, save, choices } = useConsent()
  if (!hydrated) return null
  if (!has("marketing"))
    return (
      <button type="button" onClick={() => save({ ...choices, marketing: true })}>
        Allow marketing cookies to watch the video
      </button>
    )
  return <iframe title="Northwind CRM tour" src={`https://www.youtube-nocookie.com/embed/${id}`} />
}
```

## API

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `version?` | `number` | `1` | The policy version. Bump it when the categories or the policy change: records made under another version count as undecided. |
| `storage?` | `"cookie" \| "local" \| "session"` | `"cookie"` | Where the record lives. A cookie can be read on the server too (render nothing consent-dependent on the first paint when it is missing). |
| `maxAgeDays?` | `number` | `180` | How long a cookie choice is kept before the visitor is asked again. |
| `storageKey?` | `string` | `"ui-consent"` | Cookie / storage key. Every hook with the same key shares one record. |
| `categories?` | `readonly string[]` |  | The category ids the site uses, default ["necessary", "preferences", "analytics", "marketing"]. necessary is always true. |
| `useConsent?` | `(options?) => { record: ConsentRecord \| null; decided; hydrated; choices; has(category); acceptAll(); rejectAll(); save(choices); reset() }` |  | record is { version, choices, decidedAt (ISO), method: accept-all \| reject-all \| custom } or null while undecided. hydrated is false on the server and during hydration: show the banner only once it is true and decided is false, so it never flashes for people who already chose. |

## Parts

### getConsent (function)

The same record outside React (a script loader, an analytics wrapper). Null on the server. Signature: (options?) => ConsentRecord | null.

### hasConsent (function)

The same record outside React (a script loader, an analytics wrapper). Null on the server. Signature: (category, options?) => boolean.

### setConsent (function)

Write a record outside React, and run code after every decision (load analytics when allowed, remove cookies when withdrawn). Signature: (record | null, options?) => void.

### onConsentChange (function)

Write a record outside React, and run code after every decision (load analytics when allowed, remove cookies when withdrawn). Signature: (listener: (record, key) => void) => () => void.

### openConsentSettings (function)

A "Cookie settings" link anywhere calls openConsentSettings(); the consent component listens with useConsentSettingsRequest and opens its settings. Signature: (storageKey?) => void.

### useConsentSettingsRequest (hook)

A "Cookie settings" link anywhere calls openConsentSettings(); the consent component listens with useConsentSettingsRequest and opens its settings. Signature: (handler: () => void, storageKey?) => void.

### toGoogleConsentMode (function)

Google Consent Mode v2 values for gtag("consent", "update", ...): analytics → analytics_storage, marketing → the three ad signals, preferences → functionality and personalization. Signature: (choices) => Record<string, "granted" | "denied">.

### normalizeConsent (function)

Every category present with necessary forced on; the default ids and key. Signature: (choices, categories?) => ConsentChoices.

### DEFAULT_CONSENT_CATEGORIES (constant)

Every category present with necessary forced on; the default ids and key. Type: readonly string[].

### DEFAULT_CONSENT_KEY (constant)

Every category present with necessary forced on; the default ids and key. Type: string.

## Examples

- **Consent-gated video**: has("marketing") decides between the embed and a placeholder; allowing it saves a custom record for this session only.

## See also

- [usePersistedState](https://wingo-ui.com/components/use-persisted-state) ([Markdown](https://wingo-ui.com/components/use-persisted-state.md))
- [Switch](https://wingo-ui.com/components/switch) ([Markdown](https://wingo-ui.com/components/switch.md))
- [Alert](https://wingo-ui.com/components/alert) ([Markdown](https://wingo-ui.com/components/alert.md))

---

Canonical: https://wingo-ui.com/components/use-consent
More components: https://wingo-ui.com/components · Index for agents: https://wingo-ui.com/llms.txt
