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.
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.
Installation
Section titled “Installation”npm install @probo/cookie-bannerRendering the Themed Banner
Section titled “Rendering the Themed Banner”The themed banner is a Web Component. Register it once and place the custom element in your layout.
-
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-bannerbanner-id="YOUR_BANNER_ID"base-url="https://your-probo-instance.com/api/cookie-banner/v1/"position="bottom-left"/>);} -
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.
TypeScript
Section titled “TypeScript”If TypeScript complains about the <probo-cookie-banner> JSX element, add a type declaration:
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 >; }}The useConsent Hook
Section titled “The useConsent Hook”Use the Consent Manager API to build a React hook that gives components reactive access to consent state.
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 />;}Checking a single category
Section titled “Checking a single category”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 />;}Imperative Consent Checks
Section titled “Imperative Consent Checks”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.
Next.js
Section titled “Next.js”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":
"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:
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:
"use client";
import { useConsent } from "../hooks/useConsent";
export function AnalyticsLoader() { const consent = useConsent();
if (!consent.analytics) { return null; }
return <ThirdPartyAnalytics />;}Pages Router
Section titled “Pages Router”In the Pages Router, the banner component works without "use client". Render it in _app.tsx:
import type { AppProps } from "next/app";import { CookieBanner } from "../components/CookieBanner";
export default function App({ Component, pageProps }: AppProps) { return ( <> <Component {...pageProps} /> <CookieBanner /> </> );}Headless Components
Section titled “Headless Components”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.
Settings Link
Section titled “Settings Link”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.