
Security News
CVE Volume Surges Past 48,000 in 2025 as WordPress Plugin Ecosystem Drives Growth
CVE disclosures hit a record 48,185 in 2025, driven largely by vulnerabilities in third-party WordPress plugins.
@consentry/ui
Advanced tools
Composable UI components for the Consentry consent manager. Built with Emotion and designed for use with @consentry/core or @consentry/next.
Headless and fully customizable React components for consent management. Drop-in cookie banners, settings modals, and toggles — built for Next.js with zero configuration required.
npm install @consentry/ui @consentry/next @consentry/core
Create providers/consent-provider.tsx:
"use client";
import { ConsentConfig, ConsentManagerProvider } from "@consentry/next";
import ConsentManager from "@consentry/ui";
const ConsentProvider = ({ children }: { children: React.ReactNode }) => {
const consentConfig: ConsentConfig = {
defaults: {
functional: true,
performance: false,
advertising: false,
social: false,
},
scripts: [
{
id: "gtag-js",
category: "functional",
consentRequired: false,
strategy: "afterInteractive",
src: "https://www.googletagmanager.com/gtag/js?id=YOUR_GA_ID",
},
{
id: "gtag-init",
category: "functional",
consentRequired: false,
strategy: "afterInteractive",
content: `
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'YOUR_GA_ID');
gtag('consent', 'default', {
analytics_storage: 'denied',
ad_storage: 'denied',
ad_user_data: 'denied',
ad_personalization: 'denied'
});
`,
},
],
};
return (
<ConsentManagerProvider config={consentConfig}>
<ConsentManager mode="modal" dark={false} />
{children}
</ConsentManagerProvider>
);
};
export default ConsentProvider;
Create app/providers.tsx:
"use client";
import ConsentProvider from "@/providers/consent-provider";
import { ReactNode } from "react";
export const Providers = ({ children }: { children: ReactNode }) => {
return <ConsentProvider>{children}</ConsentProvider>;
};
Update app/layout.tsx:
import { Providers } from "./providers";
import "./globals.css";
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
<Providers>{children}</Providers>
</body>
</html>
);
}
That's it! Your consent manager is now active. Users will see a banner on first visit and can manage preferences anytime.
The main UI component that handles everything automatically.
<ConsentManager mode="modal" dark={false} />
interface ConsentManagerProps {
// Layout & Positioning
mode?: "modal" | "top" | "bottom"; // Banner position
dark?: boolean; // Dark/light theme
hideSettingsButton?: boolean; // Hide floating settings button
// Customization
colors?: ColorTheme; // Custom color scheme
labels?: CustomLabels; // Custom text/translations
classNames?: CustomClassNames; // CSS class overrides
// Behavior
categories?: ConsentCategory[]; // Override default categories
autoShow?: boolean; // Auto-show banner (default: true)
showDeclineAll?: boolean; // Show "Decline All" button
showAcceptAll?: boolean; // Show "Accept All" button
}
<ConsentManager
mode="modal"
colors={{
light: {
primary: "#6B50A2",
primaryHover: "#4A2F7F",
primaryText: "#FFFFFF",
settingsButton: "#645876",
settingsButtonHover: "#403D57",
settingsButtonText: "#FFFFFF",
background: "#FFFFFF",
text: "#403D57",
border: "#D6D9E1",
},
dark: {
primary: "#8B5FD6",
primaryHover: "#A67EE5",
primaryText: "#FFFFFF",
settingsButton: "#6B7280",
settingsButtonHover: "#9CA3AF",
settingsButtonText: "#FFFFFF",
background: "#1F2937",
text: "#F9FAFB",
border: "#374151",
},
}}
/>
<ConsentManager
mode="modal"
labels={{
banner: {
title: "We value your privacy",
description: "We use cookies to enhance your experience and analyze our traffic.",
acceptAll: "Accept All",
declineAll: "Decline All",
settings: "Customize",
},
modal: {
title: "Cookie Preferences",
description: "Choose which cookies you'd like to accept.",
save: "Save Preferences",
acceptAll: "Accept All",
declineAll: "Decline All",
},
categories: {
functional: {
title: "Essential Cookies",
description: "Required for basic site functionality.",
},
performance: {
title: "Performance Cookies",
description: "Help us analyze site usage and improve performance.",
},
advertising: {
title: "Advertising Cookies",
description: "Used to deliver relevant ads and measure campaign effectiveness.",
},
social: {
title: "Social Media Cookies",
description: "Enable social media features and content sharing.",
},
},
}}
/>
// Modal overlay (recommended)
<ConsentManager mode="modal" />
// Top banner
<ConsentManager mode="top" />
// Bottom banner
<ConsentManager mode="bottom" />
interface ConsentConfig {
debug?: boolean; // Enable debug logging
defaults: CookiePreferences; // Default consent state
scripts: ConsentScript[]; // Scripts to manage
}
interface CookiePreferences {
functional: boolean; // Always true (required for site to work)
performance: boolean; // Analytics, monitoring
advertising: boolean; // Ads, marketing pixels
social: boolean; // Social media embeds, sharing
}
interface ConsentScript {
id: string; // Unique identifier
category: ConsentCategory; // Cookie category
consentRequired?: boolean; // Require explicit consent
strategy?: "afterInteractive" | "lazyOnload" | "beforeInteractive";
src?: string; // External script URL
content?: string; // Inline script content
noscript?: string; // Fallback for no-JS
vendor?: string; // Third-party service name
default?: boolean; // Load by default
}
scripts: [
{
id: "gtag-js",
category: "performance",
consentRequired: true,
strategy: "afterInteractive",
src: "https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX",
vendor: "Google Analytics",
},
{
id: "gtag-init",
category: "performance",
consentRequired: true,
strategy: "afterInteractive",
content: `
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-XXXXXXXXXX', {
send_page_view: true,
cookie_flags: 'SameSite=None;Secure'
});
// Required for Google Analytics v2 consent mode - must start with 'denied'
// These will be updated to 'granted' when user provides consent
gtag('consent', 'default', {
analytics_storage: 'denied',
ad_storage: 'denied',
ad_user_data: 'denied',
ad_personalization: 'denied'
});
`,
vendor: "Google Analytics",
},
];
{
id: "facebook-pixel",
category: "advertising",
consentRequired: true,
strategy: "afterInteractive",
content: `
!function(f,b,e,v,n,t,s)
{if(f.fbq)return;n=f.fbq=function(){n.callMethod?
n.callMethod.apply(n,arguments):n.queue.push(arguments)};
if(!f._fbq)f._fbq=n;n.push=n;n.loaded=!0;n.version='2.0';
n.queue=[];t=b.createElement(e);t.async=!0;
t.src=v;s=b.getElementsByTagName(e)[0];
s.parentNode.insertBefore(t,s)}(window, document,'script',
'https://connect.facebook.net/en_US/fbevents.js');
fbq('init', 'YOUR_PIXEL_ID');
fbq('track', 'PageView');
`,
noscript: `<img height="1" width="1" style="display:none" src="https://www.facebook.com/tr?id=YOUR_PIXEL_ID&ev=PageView&noscript=1" />`,
vendor: "Meta",
}
{
id: "hotjar",
category: "performance",
consentRequired: true,
strategy: "afterInteractive",
content: `
(function(h,o,t,j,a,r){
h.hj=h.hj||function(){(h.hj.q=h.hj.q||[]).push(arguments)};
h._hjSettings={hjid:YOUR_HOTJAR_ID,hjsv:6};
a=o.getElementsByTagName('head')[0];
r=o.createElement('script');r.async=1;
r.src=t+h._hjSettings.hjid+j+h._hjSettings.hjsv;
a.appendChild(r);
})(window,document,'https://static.hotjar.com/c/hotjar-','.js?sv=');
`,
vendor: "Hotjar",
}
{
id: "youtube-embeds",
category: "social",
consentRequired: true,
strategy: "lazyOnload",
content: `
// Enable YouTube embeds when social cookies are accepted
document.querySelectorAll('[data-youtube-consent]').forEach(el => {
el.style.display = 'block';
});
`,
vendor: "YouTube",
}
{
id: "twitter-widgets",
category: "social",
consentRequired: true,
strategy: "lazyOnload",
src: "https://platform.twitter.com/widgets.js",
vendor: "Twitter/X",
}
import { openConsentSettings } from "@consentry/ui";
function PrivacyPage() {
return (
<div>
<h1>Privacy Policy</h1>
<button onClick={() => openConsentSettings()}>Manage Cookie Preferences</button>
</div>
);
}
import { useConsentManager } from "@consentry/next";
function MyComponent() {
const { preferences, updatePreferences } = useConsentManager();
return (
<div>
<p>Analytics enabled: {preferences.performance ? "Yes" : "No"}</p>
<button onClick={() => updatePreferences({ performance: !preferences.performance })}>
Toggle Analytics
</button>
</div>
);
}
classNames={{
// Main wrapper
wrapper: "consent-wrapper",
// Banner classes
banner: {
container: "cookie-banner",
header: "cookie-banner-header",
title: "cookie-banner-title",
message: "cookie-banner-message",
closeButton: "cookie-banner-close-button",
buttonRow: "cookie-banner-button-row",
acceptButton: "cookie-banner-accept-button",
rejectButton: "cookie-banner-reject-button",
customizeButton: "cookie-banner-customize-button",
content: "cookie-banner-content",
},
// Modal classes
modal: {
overlay: "cookie-modal-overlay",
container: "cookie-modal-container",
title: "cookie-modal-title",
section: "cookie-modal-section",
row: "cookie-modal-row",
rowText: "cookie-modal-row-text",
rowTitle: "cookie-modal-row-title",
rowDescription: "cookie-modal-row-description",
toggleSwitch: "cookie-modal-switch",
toggleThumb: "cookie-modal-switch-thumb",
buttonRow: "cookie-modal-button-row",
saveButton: "cookie-modal-save-button",
cancelButton: "cookie-modal-cancel-button",
},
// Settings button
settingsButton: "cookie-settings-button",
}}
Example: Custom styling
/* Style the banner */
.cookie-banner {
border-radius: 12px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
border: 1px solid #e5e7eb;
}
.cookie-banner-title {
font-weight: 700;
font-size: 1.2rem;
color: #1f2937;
}
.cookie-banner-accept-button {
background: #6b50a2;
color: white;
border-radius: 6px;
padding: 8px 16px;
}
/* Style the modal */
.cookie-modal-overlay {
backdrop-filter: blur(4px);
}
.cookie-modal-container {
border-radius: 16px;
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1);
}
.cookie-modal-switch {
background: #e5e7eb;
transition: all 0.2s;
}
.cookie-modal-switch[data-checked="true"] {
background: #6b50a2;
}
/* Style the settings button */
.cookie-settings-button {
border-radius: 50%;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
background: #6b50a2;
color: white;
}
const getLabels = (locale: string) => {
const translations = {
en: {
banner: {
title: "We value your privacy",
description: "We use cookies to enhance your experience.",
acceptAll: "Accept All",
declineAll: "Decline All",
settings: "Settings",
},
},
es: {
banner: {
title: "Valoramos tu privacidad",
description: "Utilizamos cookies para mejorar tu experiencia.",
acceptAll: "Aceptar Todo",
declineAll: "Rechazar Todo",
settings: "Configuración",
},
},
};
return translations[locale] || translations.en;
};
// Usage
<ConsentManager labels={getLabels("es")} />;
const consentConfig: ConsentConfig = {
debug: process.env.NODE_ENV === "development",
defaults: {
functional: true,
performance: process.env.NODE_ENV === "production",
advertising: false,
social: false,
},
scripts: process.env.NODE_ENV === "production" ? productionScripts : [],
};
// ❌ Wrong - script won't be managed
<Script src="https://analytics.example.com/script.js" />;
// ✅ Correct - add to consent config
scripts: [
{
id: "analytics",
category: "performance",
consentRequired: true,
strategy: "afterInteractive",
src: "https://analytics.example.com/script.js",
},
];
// ❌ Wrong - CSS loaded after component
import ConsentManager from "@consentry/ui";
import "./consent-styles.css";
// ✅ Correct - CSS loaded first
import "./consent-styles.css";
import ConsentManager from "@consentry/ui";
// ❌ Wrong - server/client mismatch
const [mounted, setMounted] = useState(false);
useEffect(() => setMounted(true), []);
if (!mounted) return null;
// ✅ Correct - use dynamic import
import dynamic from "next/dynamic";
const ConsentManager = dynamic(() => import("@consentry/ui"), { ssr: false });
Complete setup with custom styling, multiple analytics, and internationalization:
"use client";
import { ConsentConfig, ConsentManagerProvider } from "@consentry/next";
import ConsentManager from "@consentry/ui";
import { useRouter } from "next/navigation";
const ConsentProvider = ({ children }: { children: React.ReactNode }) => {
const router = useRouter();
const consentConfig: ConsentConfig = {
debug: process.env.NODE_ENV === "development",
defaults: {
functional: true,
performance: false,
advertising: false,
social: false,
},
scripts: [
// Google Analytics
{
id: "gtag-js",
category: "performance",
consentRequired: true,
strategy: "afterInteractive",
src: "https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX",
vendor: "Google Analytics",
},
{
id: "gtag-init",
category: "performance",
consentRequired: true,
strategy: "afterInteractive",
content: `
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-XXXXXXXXXX');
gtag('consent', 'default', {
analytics_storage: 'denied',
ad_storage: 'denied'
});
`,
vendor: "Google Analytics",
},
// Facebook Pixel
{
id: "facebook-pixel",
category: "advertising",
consentRequired: true,
strategy: "afterInteractive",
content: `
// Facebook Pixel code here
`,
vendor: "Meta",
},
// Hotjar
{
id: "hotjar",
category: "performance",
consentRequired: true,
strategy: "afterInteractive",
content: `
// Hotjar code here
`,
vendor: "Hotjar",
},
],
};
return (
<ConsentManagerProvider config={consentConfig}>
<ConsentManager
mode="modal"
dark={false}
colors={{
light: {
primary: "#6B50A2",
primaryHover: "#4A2F7F",
primaryText: "#FFFFFF",
settingsButton: "#645876",
settingsButtonHover: "#403D57",
settingsButtonText: "#FFFFFF",
background: "#FFFFFF",
text: "#403D57",
border: "#D6D9E1",
},
}}
labels={{
banner: {
title: "We respect your privacy",
description:
"We use cookies to improve your experience and analyze our website traffic. You can customize your preferences below.",
acceptAll: "Accept All Cookies",
declineAll: "Decline All",
settings: "Customize Settings",
},
modal: {
title: "Cookie Preferences",
description:
"We use different types of cookies to optimize your experience on our website. You can customize your preferences for each category below.",
save: "Save My Preferences",
acceptAll: "Accept All",
declineAll: "Decline All",
},
}}
classNames={{
banner: "rounded-lg shadow-xl",
bannerTitle: "text-xl font-bold",
settingsButton: "rounded-full shadow-lg",
}}
/>
{children}
</ConsentManagerProvider>
);
};
export default ConsentProvider;
@consentry/core — Core consent logic (framework-agnostic)@consentry/next — Next.js integration hooks and providersMIT — Copyright © 2025 Mustafa ONAL
FAQs
Composable UI components for the Consentry consent manager. Built with Emotion and designed for use with @consentry/core or @consentry/next.
We found that @consentry/ui demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
CVE disclosures hit a record 48,185 in 2025, driven largely by vulnerabilities in third-party WordPress plugins.

Security News
Socket CEO Feross Aboukhadijeh joins Insecure Agents to discuss CVE remediation and why supply chain attacks require a different security approach.

Security News
Tailwind Labs laid off 75% of its engineering team after revenue dropped 80%, as LLMs redirect traffic away from documentation where developers discover paid products.