libconsent
Adapters

IAB GPP

US state privacy signals, through the __gppapi.

npm i @libconsent/gpp
import { ConsentProvider } from "@libconsent/core";
import { gpp } from "@libconsent/gpp";

<ConsentProvider adapter={gpp()}>{children}</ConsentProvider>;

The Global Privacy Platform carries US state privacy signals. Like TCF, it is a standard rather than a vendor: the adapter reads whichever GPP-capable CMP is already on the page.

GPP is opt-out, not opt-in

US state laws work the other way round from the GDPR. Nothing is blocked until the user opts out, so every category starts granted and a sale, sharing or targeted-advertising opt-out takes marketing away. If you are looking for "nothing runs until they say yes", you want an opt-in CMP, not this.

Options

OptionDefault
categories—Decide categories yourself from the parsed sections.
analyticsFollowsSalefalseTreat a sale/sharing opt-out as an analytics opt-out too.
globalPrivacyControltrueHonour a GPC signal in the string as an opt-out.
commands—GPP is a transport, not a UI. Wire your CMP's calls in.

What gets read

Every US section the CMP reports — usnat, usca, usva and the rest — is checked for these fields:

FieldSet to 1 means
SaleOptOutDo not sell my data
SharingOptOutDo not share it for ads
TargetedAdvertisingOptOutNo targeted advertising
GpcBrowser-level opt-out

Any one of them is enough to deny marketing. The matching *Notice fields are deliberately ignored: they record that a disclosure was shown, not what the user chose.

analytics and functional stay granted, because US state laws generally treat first-party measurement as something you may do until told otherwise. Sites that would rather be strict can say so:

gpp({ analyticsFollowsSale: true });

Deciding for yourself

The parsed sections are handed to you if the defaults don't fit. Return only the categories you want to change:

gpp({
  categories: (sections, { gpc }) => ({
    analytics: !gpc,
    marketing: sections.usca?.SaleOptOut !== 1,
  }),
});

Serving the EU and the US

An adapter is a prop, so pick one per request rather than trying to make a single adapter cover both regimes:

import { gpp } from "@libconsent/gpp";
import { tcf } from "@libconsent/tcf";

const adapter = React.useMemo(
  () => (region === "us" ? gpp() : tcf()),
  [region],
);

<ConsentProvider adapter={adapter}>{children}</ConsentProvider>;

Build the adapter in a useMemo — a new adapter object means a new connection.

Everything below the provider — useConsent(), <CookieService>, openSettings() — is identical either way. That is the point.

Timing

Nothing is published until the CMP reports signalStatus: "ready". Before that it may still change its mind, and a gated component that appears and then disappears is worse than one that appears a moment late.

On this page