IAB GPP
US state privacy signals, through the __gppapi.
npm i @libconsent/gppimport { 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
| Option | Default | |
|---|---|---|
categories | — | Decide categories yourself from the parsed sections. |
analyticsFollowsSale | false | Treat a sale/sharing opt-out as an analytics opt-out too. |
globalPrivacyControl | true | Honour 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:
| Field | Set to 1 means |
|---|---|
SaleOptOut | Do not sell my data |
SharingOptOut | Do not share it for ads |
TargetedAdvertisingOptOut | No targeted advertising |
Gpc | Browser-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.