Skip to content

Products

Compliance Officer Service Expert-led compliance, end to end Compliance Portal Share security documents securely Open-source platform Deploy Probo on your own infrastructure

Resources

Probo stories How teams get compliant with Probo Blog Ideas and guidance from the Probo team Guides & tools Practical compliance guides and free tools Love from Customers What customers say about working with Probo Changelog Latest product updates Download Get the Probo Agent

Company

About The people and vision powering Probo Careers Join the team building Probo Brand assets Official logos and visual resources Security Review our security and compliance posture
Overview Understand Probo and its core concepts Product Explore Probo's GRC capabilities Developers Explore GraphQL, CLI, MCP, n8n, and webhooks Deployment Probo Cloud, self-hosting, and configuration

Explore

GitHub Explore our open-source compliance tools

React Integration

Integrate the Probo cookie banner into React or Next.js, including a useConsent hook, TypeScript declarations, and App Router or Pages Router setup.

View as Markdown

This guide shows how to integrate the Probo cookie banner into a React application, build a useConsent hook for consent-aware rendering, and handle Next.js-specific requirements.

Terminal window
npm install @probo/cookie-banner

The themed banner is a Web Component. Register it once and place the custom element in your layout.

  1. Create a CookieBanner component
    src/components/CookieBanner.tsx
    import { useEffect } from "react";
    import { registerCookieBanner } from "@probo/cookie-banner";
    let registered = false;
    export function CookieBanner() {
    useEffect(() => {
    if (!registered) {
    registerCookieBanner();
    registered = true;
    }
    }, []);
    return (
    <probo-cookie-banner
    banner-id="YOUR_BANNER_ID"
    base-url="https://your-probo-instance.com/api/cookie-banner/v1/"
    position="bottom-left"
    />
    );
    }
  2. Add it to your layout with a settings link

    Render <CookieBanner /> once at the root of your application, and place <probo-settings-link> in your header or footer so visitors can reopen preferences:

    src/App.tsx
    import { CookieBanner } from "./components/CookieBanner";
    export default function App() {
    return (
    <>
    {/* your app */}
    <CookieBanner />
    <footer>
    <probo-settings-link>Cookie settings</probo-settings-link>
    </footer>
    </>
    );
    }

The banner handles everything from there — it fetches the configuration, shows the consent dialog when needed, and records consent. In opt-out jurisdictions the dialog never opens on its own and the settings link is the visitor’s only route to their choices; under CCPA that link shows “Your Privacy Choices” with the official opt-out icon and opens the Privacy Choices panel.

If TypeScript complains about the <probo-cookie-banner> JSX element, add a type declaration:

src/probo.d.ts
declare namespace JSX {
interface IntrinsicElements {
"probo-cookie-banner": React.DetailedHTMLProps<
React.HTMLAttributes<HTMLElement> & {
"banner-id": string;
"base-url": string;
position?:
| "bottom-left"
| "bottom-right"
| "bottom-center"
| "top-left"
| "top-right"
| "top-center";
lang?: string;
},
HTMLElement
>;
"probo-settings-link": React.DetailedHTMLProps<
React.HTMLAttributes<HTMLElement>,
HTMLElement
>;
"probo-acknowledge-button": React.DetailedHTMLProps<
React.HTMLAttributes<HTMLElement>,
HTMLElement
>;
"probo-privacy-choices": React.DetailedHTMLProps<
React.HTMLAttributes<HTMLElement>,
HTMLElement
>;
}
}

Use the Consent Manager API to build a React hook that gives components reactive access to consent state.

src/hooks/useConsent.ts
import { useSyncExternalStore } from "react";
import { getConsent } from "@probo/cookie-banner/consent";
const consent = getConsent();
function subscribe(onStoreChange: () => void): () => void {
return consent.subscribe(onStoreChange);
}
function getSnapshot(): Record<string, boolean> {
return consent.getAll();
}
function getServerSnapshot(): Record<string, boolean> {
return {};
}
export function useConsent(): Record<string, boolean> {
return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
}

This hook re-renders the component whenever consent state changes — on initial resolution and after the visitor updates their preferences.

import { useConsent } from "../hooks/useConsent";
export function AnalyticsLoader() {
const consent = useConsent();
if (!consent.analytics) {
return null;
}
return <ThirdPartyAnalytics />;
}
import { useConsent } from "../hooks/useConsent";
export function ChatWidget() {
const consent = useConsent();
if (!consent.functional) {
return <p>Enable functional cookies to use live chat.</p>;
}
return <LiveChat />;
}

For code that runs outside of React’s render cycle — event handlers, side effects, or non-React modules — use getConsent() directly instead of the hook:

import { getConsent } from "@probo/cookie-banner/consent";
export function trackEvent(name: string) {
if (getConsent().has("analytics")) {
analytics.track(name);
}
}

See the Consent Manager API reference for the full API.

The cookie banner SDK accesses document, window, and localStorage, so it must run on the client. In the Next.js App Router, mark the component with "use client":

src/components/CookieBanner.tsx
"use client";
import { useEffect } from "react";
import { registerCookieBanner } from "@probo/cookie-banner";
let registered = false;
export function CookieBanner() {
useEffect(() => {
if (!registered) {
registerCookieBanner();
registered = true;
}
}, []);
return (
<probo-cookie-banner
banner-id="YOUR_BANNER_ID"
base-url="https://your-probo-instance.com/api/cookie-banner/v1/"
position="bottom-left"
/>
);
}

Then render it in your root layout:

src/app/layout.tsx
import { CookieBanner } from "../components/CookieBanner";
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
{children}
<CookieBanner />
</body>
</html>
);
}

The useConsent hook works in any client component. Mark hooks that use it with "use client" or consume them from client components:

src/components/AnalyticsLoader.tsx
"use client";
import { useConsent } from "../hooks/useConsent";
export function AnalyticsLoader() {
const consent = useConsent();
if (!consent.analytics) {
return null;
}
return <ThirdPartyAnalytics />;
}

In the Pages Router, the banner component works without "use client". Render it in _app.tsx:

pages/_app.tsx
import type { AppProps } from "next/app";
import { CookieBanner } from "../components/CookieBanner";
export default function App({ Component, pageProps }: AppProps) {
return (
<>
<Component {...pageProps} />
<CookieBanner />
</>
);
}

For complete control over the consent UI in React, use the headless components. Register them once and compose the Web Component building blocks in your JSX:

"use client";
import { useEffect } from "react";
import { registerHeadlessComponents } from "@probo/cookie-banner/headless";
let registered = false;
export function CustomConsentBanner() {
useEffect(() => {
if (!registered) {
registerHeadlessComponents();
registered = true;
}
}, []);
return (
<>
<probo-cookie-banner-root
banner-id="YOUR_BANNER_ID"
base-url="https://your-probo-instance.com/api/cookie-banner/v1/"
>
<probo-banner>
<div className="my-banner">
<p>We use cookies to improve your experience.</p>
<probo-accept-button>
<button>Accept all</button>
</probo-accept-button>
<probo-reject-button>
<button>Reject all</button>
</probo-reject-button>
<probo-customize-button>
<button>Customize</button>
</probo-customize-button>
<probo-acknowledge-button>
<button>Got it</button>
</probo-acknowledge-button>
</div>
</probo-banner>
<probo-preference-panel>
<div className="my-preferences">
<probo-category-list>
<template>
<div className="category">
<span data-slot="name"></span>
<span data-slot="description"></span>
<probo-category-toggle>
<input type="checkbox" />
</probo-category-toggle>
</div>
</template>
</probo-category-list>
<probo-save-button>
<button>Save preferences</button>
</probo-save-button>
</div>
</probo-preference-panel>
<probo-privacy-choices>
<div className="my-privacy-choices">
<probo-reject-button>
<button>Do Not Sell or Share My Personal Information</button>
</probo-reject-button>
</div>
</probo-privacy-choices>
</probo-cookie-banner-root>
<probo-settings-link>Cookie settings</probo-settings-link>
</>
);
}

Use resolveLayout / resolveBannerText from @probo/cookie-banner/headless to show the right controls for OPT_IN, OPT_OUT, or NOTICE. See the JavaScript SDK docs for the full component reference and Layout API.

Place <probo-settings-link> in your header or footer. It is required for all embeds — there is no floating settings button, and in opt-out jurisdictions it is the only banner surface a visitor ever sees.

export function Footer() {
return (
<footer>
<probo-settings-link>Cookie settings</probo-settings-link>
</footer>
);
}

Put your default label as children (shown for non-CCPA visitors). Under CCPA the SDK always replaces the content with the statutory “Your Privacy Choices” label and official opt-out icon, and opens the Privacy Choices panel. Style the host for font size and color:

probo-settings-link {
font-size: 14px;
color: #334155;
text-decoration: underline;
}

See Settings link for full behavior by regulation.