npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@consentry/ui

v0.2.5

Published

Composable UI components for the Consentry consent manager. Built with Emotion and designed for use with @consentry/core or @consentry/next.

Readme

@consentry/ui

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.


✨ What You Get

  • 🍪 Complete cookie consent solution — banner, modal, settings
  • 🎨 Fully customizable — colors, text, positioning, behavior
  • Zero config required — works out of the box with sensible defaults
  • 🧩 Headless architecture — style it your way or use built-in themes
  • 🔄 Smooth animations — powered by Framer Motion
  • Accessibility first — keyboard navigation, screen readers, focus management
  • 📱 Mobile optimized — responsive design that works everywhere

📦 Installation

npm install @consentry/ui @consentry/next @consentry/core

🚀 Quick Start (5 minutes)

Step 1: Create the Consent Provider

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;

Step 2: Add to Your App

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>;
};

Step 3: Wrap Your App

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.


🎛️ ConsentManager Component

The main UI component that handles everything automatically.

Basic Usage

<ConsentManager mode="modal" dark={false} />

All Props

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
}

🎨 Customization Examples

Custom Colors

<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",
    },
  }}
/>

Custom Text/Labels

<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.",
      },
    },
  }}
/>

Banner Positions

// Modal overlay (recommended)
<ConsentManager mode="modal" />

// Top banner
<ConsentManager mode="top" />

// Bottom banner
<ConsentManager mode="bottom" />

⚙️ Configuration Types

ConsentConfig

interface ConsentConfig {
  debug?: boolean; // Enable debug logging
  defaults: CookiePreferences; // Default consent state
  scripts: ConsentScript[]; // Scripts to manage
}

CookiePreferences

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
}

ConsentScript

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
}

📜 Script Examples

Google Analytics 4

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",
  },
];

Facebook Pixel

{
  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",
}

Hotjar

{
  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",
}

YouTube Embeds

{
  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",
}

Twitter/X Embeds

{
  id: "twitter-widgets",
  category: "social",
  consentRequired: true,
  strategy: "lazyOnload",
  src: "https://platform.twitter.com/widgets.js",
  vendor: "Twitter/X",
}

🎛️ Programmatic Control

Open Settings Modal

import { openConsentSettings } from "@consentry/ui";

function PrivacyPage() {
  return (
    <div>
      <h1>Privacy Policy</h1>
      <button onClick={() => openConsentSettings()}>Manage Cookie Preferences</button>
    </div>
  );
}

Get Current Preferences

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>
  );
}

🎨 Advanced Styling

CSS Classes Available

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;
}

🌍 Internationalization

Multi-language Support

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")} />;

🔧 Environment-Specific Configs

Development vs Production

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 : [],
};

🚨 Common Issues & Solutions

Scripts Not Loading

// ❌ 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",
  },
];

Styling Not Applied

// ❌ 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";

Hydration Issues

// ❌ 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 });

📚 Full Example

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;

🔗 Related Packages


📄 License

MIT — Copyright © 2025 Mustafa ONAL