React SDK
Integrate ConsentStack into React and Next.js apps with hooks and components.
React bindings for ConsentStack. Use hooks to read consent state, conditionally render components, and build custom consent UIs — all with full TypeScript support.
Installation
npm install @consentstack/reactPeer dependencies: react and react-dom ^18 or ^19.
Quick start
Choose the approach that fits your needs:
| Approach | When to use |
|---|---|
ConsentStack | You just need the banner. No hooks, no conditional rendering. |
ConsentStackProvider | You need hooks to read consent state, conditionally load scripts, or build a custom UI. |
Simple integration
Install the package
pnpm add @consentstack/reactAdd to your layout
import { ConsentStack } from '@consentstack/react'
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
{children}
<ConsentStack siteKey="pk_abc123" />
</body>
</html>
)
}That's it — the consent banner renders automatically.
With hooks
Wrap your app with the provider
import { ConsentStackProvider } from '@consentstack/react'
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
<ConsentStackProvider siteKey="pk_abc123">
{children}
</ConsentStackProvider>
</body>
</html>
)
}Use consent in any component
import { useConsent } from '@consentstack/react'
function CookieStatus() {
const { hasConsent, showPreferences, error } = useConsent()
if (error) return <p>Failed to load consent: {error.message}</p>
return (
<div>
<p>Analytics: {hasConsent('analytics') ? 'Allowed' : 'Denied'}</p>
<button onClick={() => showPreferences()}>
Manage preferences
</button>
</div>
)
}Component props
ConsentStack
| Prop | Type | Default | Description |
|---|---|---|---|
siteKey | string | required | Your site's public key from the ConsentStack dashboard. |
debug | boolean | false | Log SDK initialization and consent events to the console. |
cdnUrl | string | "https://cdn.consentstack.io/consent.js" | Override the SDK script URL (useful for self-hosting). |
timeout | number | 15000 | Timeout in milliseconds for SDK script loading. |
ConsentStackProvider
Accepts all ConsentStack props plus:
| Prop | Type | Default | Description |
|---|---|---|---|
mode | "banner" | "headless" | "banner" | "banner" shows the consent UI automatically. "headless" hides it so you can build your own. |
children | ReactNode | required | Your app tree. |
Hooks
useConsent
Returns the full consent state and control methods. Must be used within a ConsentStackProvider.
interface UseConsentReturn {
/** Current consent state, or null if not yet given */
consent: Record<string, boolean> | null
/** The loaded consent config, or null if not loaded */
config: ConsentConfig | null
/** True while the SDK is initializing */
isLoading: boolean
/** True once the SDK is ready and consent state is known */
isReady: boolean
/** Error if SDK failed to load, null otherwise */
error: Error | null
/** Update consent programmatically — logs to the server */
setConsent: (categories: Record<string, boolean>) => Promise<void>
/** Check if a specific category has consent */
hasConsent: (category: string) => boolean
/** Programmatically show the consent banner */
showBanner: () => void
/** Open the preferences panel */
showPreferences: () => void
/** Close the preferences panel */
hidePreferences: () => void
/** Whether the preferences panel is currently open */
isPreferencesOpen: boolean
}The config object uses the ConsentConfig type from the JS SDK — see the JavaScript API reference for the full shape.
useConsentValue
An optimized hook for checking a single consent category. Uses useSyncExternalStore under the hood, so your component only re-renders when that specific category value changes.
function useConsentValue(category: string): booleanReturns true if the category has consent, false otherwise. Returns false during SSR and while the SDK is loading — a safe default that prevents scripts from firing before consent is confirmed.
import { useConsentValue } from '@consentstack/react'
function AnalyticsLoader() {
const hasAnalytics = useConsentValue('analytics')
if (!hasAnalytics) return null
return <script src="https://www.googletagmanager.com/gtag/js?id=G-XXXXX" async />
}useConsentEvent
Subscribe to SDK lifecycle events. Automatically subscribes on mount and unsubscribes on unmount. You don't need to memoize the callback — the hook handles that internally via a ref.
function useConsentEvent<T extends ConsentEventType>(
event: T,
callback: (data: ConsentEventData[T]) => void
): void| Event | Payload | Fires when |
|---|---|---|
"ready" | { config, consent } | SDK has initialized and config is loaded. |
"consent" | Record<string, boolean> | Consent state changes. |
"error" | Error | An SDK error occurs. |
"preferences:open" | undefined | Preferences panel opens. |
"preferences:close" | undefined | Preferences panel closes. |
import { useConsentEvent } from '@consentstack/react'
function PreferencesTracker() {
useConsentEvent('preferences:open', () => {
analytics.track('consent_preferences_opened')
})
useConsentEvent('error', (error) => {
console.error('SDK error:', error)
})
return null
}Error handling
The provider exposes errors from SDK initialization. If the CDN is unreachable or the script fails to load, error will contain the reason.
function ConsentFallback() {
const { error, isReady } = useConsent()
if (error) {
return <p>Consent system unavailable. Some features may be limited.</p>
}
if (!isReady) return null
return <YourApp />
}The SDK times out after 15 seconds by default. If the CDN is slow or blocked, isLoading will become false and error will contain a descriptive timeout message. Customize the timeout with the timeout prop on ConsentStackProvider or ConsentStack.
Common patterns
Conditional script loading
Block third-party scripts until the visitor grants consent:
import { useConsentValue } from '@consentstack/react'
import Script from 'next/script'
function ThirdPartyScripts() {
const analyticsAllowed = useConsentValue('analytics')
const marketingAllowed = useConsentValue('marketing')
return (
<>
{analyticsAllowed && (
<Script src="https://www.googletagmanager.com/gtag/js?id=G-XXXXX" strategy="afterInteractive" />
)}
{marketingAllowed && (
<Script src="https://connect.facebook.net/en_US/fbevents.js" strategy="afterInteractive" />
)}
</>
)
}Custom preferences UI (headless mode)
Set mode="headless" on the provider to suppress the default banner entirely. Then build your own UI using the hook methods:
<ConsentStackProvider siteKey="pk_abc123" mode="headless">
{children}
</ConsentStackProvider>import { useConsent } from '@consentstack/react'
function CookiePreferences() {
const { consent, config, setConsent, isReady, showPreferences, hidePreferences } = useConsent()
if (!isReady || !config) return null
const handleToggle = (categoryId: string, enabled: boolean) => {
setConsent({ ...consent, [categoryId]: enabled })
}
return (
<div>
<h2>Cookie Preferences</h2>
{config.categories.map((cat) => (
<label key={cat.id}>
<input
type="checkbox"
checked={consent?.[cat.id] ?? cat.default}
disabled={cat.required}
onChange={(e) => handleToggle(cat.id, e.target.checked)}
/>
<span>{cat.name}</span>
<p>{cat.description}</p>
</label>
))}
</div>
)
}In headless mode, you can call showBanner() to display the default ConsentStack banner on demand — useful as a fallback while you build your custom UI.
TypeScript
All exports are fully typed. The key types you can import:
import type {
ConsentConfig,
ConsentCategory,
ConsentStackAPI,
ConsentEventType,
ConsentEventData,
UseConsentReturn,
ConsentStackProps,
ConsentStackProviderProps,
} from '@consentstack/react'ConsentConfig, ConsentCategory, ConsentStackAPI, ConsentEventType, and ConsentEventData are re-exported from the JS SDK — the React package is always in sync.
What's next
- Script blocking — how ConsentStack prevents scripts from firing before consent
- Categories and regions — configuring which consent categories apply to which regions
- JavaScript API — full reference for the underlying SDK