libconsent
Adapters

Klaro

Klaro — open source, service-first, and writable from your own UI.

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

<ConsentProvider adapter={klaro({ config: klaroConfig })}>
  {children}
</ConsentProvider>;

Klaro is open source and self-hostable, and it is service-first: its config lists each service and the purposes that service serves. That maps onto libconsent exactly — and because Klaro accepts consent back, it is one of the platforms where the shadcn banner and useConsentForm() can replace its UI entirely.

Options

OptionDefault
configwindow.klaroConfigKlaro's own config. Set here, it lands on window.klaroConfig before the script boots.
srckiprotect CDNWhere to load Klaro from.
manualfalseThe script is already on the page.
purposeskeyword matchingMap Klaro purposes onto libconsent categories.

Services and purposes

A typical config:

klaro.config.ts
export const klaroConfig = {
  services: [
    {
      name: "core",
      title: "Site basics",
      purposes: ["necessary"],
      required: true,
    },
    { name: "matomo", title: "Matomo", purposes: ["analytics"] },
    { name: "intercom", title: "Intercom", purposes: ["livechat"] },
  ],
};

Every service answers under both its name and its title, so the gate finds it either way:

<CookieService id="matomo">
  <Matomo />
</CookieService>

Klaro's purposes are free text, so they are matched on what they contain — analytics and advertising land where you'd expect, and anything unrecognised is treated as functional. Be explicit when that guess is wrong:

klaro({ config: klaroConfig, purposes: { livechat: "functional" } });

Klaro's own purpose names are published as categories too, so a purpose that has no libconsent equivalent is still gateable:

const chat = useHasConsent("livechat");

Klaro is one of the few platforms where libconsent can own the UI. Leave out its own modal and render the shadcn banner instead:

<ConsentProvider adapter={klaro({ config: klaroConfig })}>
  {children}
  <ConsentBanner privacyPolicyUrl="/privacy" />
</ConsentProvider>

A category-level answer is written through to every service under it. Services Klaro marks required are left alone — the config author already said they are not optional.

Klaro does not write the Consent Mode signals, so libconsent does it — see Google Consent Mode.

On this page