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/next

v0.1.21

Published

Next.js + React integration layer for the Consentry SDK. Manages cookie preferences, script injection, and analytics sync.

Readme

@consentry/next

Next.js integration for Consentry consent management. React hooks, providers, and automatic script injection — works seamlessly with @consentry/ui for a complete solution.


✨ What You Get

  • ⚛️ React Context Provider — manages consent state across your app
  • 🪝 Custom hooksuseConsentManager() for easy state access
  • 📜 Automatic script injection — loads scripts based on user consent
  • 🔄 Google Analytics integration — automatic consent mode v2 updates
  • 🎨 UI component ready — pairs perfectly with @consentry/ui
  • 🚀 Next.js optimized — works with App Router and Pages Router

📦 Installation

Complete Solution (Recommended)

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

Core Integration Only

npm install @consentry/next @consentry/core

🚀 Quick Start (Complete Solution)

👑 This is the recommended approach — using @consentry/next with @consentry/ui gives you a complete, plug-and-play consent management solution.

Step 1: Create 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 = {
    debug: process.env.NODE_ENV === "development",
    defaults: {
      functional: true,
      performance: false,
      advertising: false,
      social: false,
    },
    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');
          gtag('consent', 'default', {
            analytics_storage: 'denied',
            ad_storage: 'denied',
            ad_user_data: 'denied',
            ad_personalization: 'denied'
          });
        `,
        vendor: "Google Analytics",
      },
    ],
  };

  return (
    <ConsentManagerProvider config={consentConfig}>
      <ConsentManager mode="modal" dark={false} />
      {children}
    </ConsentManagerProvider>
  );
};

export default ConsentProvider;

Step 2: Add to Your App

App Router (app/layout.tsx):

import ConsentProvider from "@/providers/consent-provider";
import "./globals.css";

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        <ConsentProvider>{children}</ConsentProvider>
      </body>
    </html>
  );
}

Pages Router (pages/_app.tsx):

import ConsentProvider from "@/providers/consent-provider";
import type { AppProps } from "next/app";

export default function App({ Component, pageProps }: AppProps) {
  return (
    <ConsentProvider>
      <Component {...pageProps} />
    </ConsentProvider>
  );
}

Step 3: Use Anywhere in Your App

"use client";
import { useConsentManager } from "@consentry/next";

export default function PrivacySettings() {
  const { preferences, updatePreferences, hasConsentedOnce } = useConsentManager();

  if (!hasConsentedOnce) {
    return <p>Please accept or decline cookies first.</p>;
  }

  return (
    <div>
      <h2>Your Privacy Settings</h2>
      <label>
        <input
          type="checkbox"
          checked={preferences.performance}
          onChange={e => updatePreferences({ ...preferences, performance: e.target.checked })}
        />
        Analytics & Performance
      </label>
      <label>
        <input
          type="checkbox"
          checked={preferences.advertising}
          onChange={e => updatePreferences({ ...preferences, advertising: e.target.checked })}
        />
        Advertising & Marketing
      </label>
    </div>
  );
}

That's it! You now have a complete consent management system with banner, modal, and programmatic control.


🎛️ Core Components

ConsentManagerProvider

The main provider that manages all consent state and script injection.

interface ConsentManagerProviderProps {
  config: ConsentConfig; // Your consent configuration
  children: React.ReactNode; // Your app content
  storageKey?: string; // Custom localStorage key (default: 'consentry-preferences')
  debug?: boolean; // Enable debug logging
}

useConsentManager Hook

Access consent state and controls from any component.

const {
  preferences, // Current user preferences
  updatePreferences, // Update all preferences
  setCategoryConsent, // Update single category
  hasConsentedTo, // Check specific category
  hasConsentedOnce, // Has user made any choice?
  acceptAll, // Accept all categories
  rejectAll, // Reject all (except functional)
  resetConsent, // Clear all consent data
} = useConsentManager();

📋 Complete API Reference

ConsentConfig

interface ConsentConfig {
  debug?: boolean; // Enable debug logging
  defaults: CookiePreferences; // Default consent state
  scripts: ConsentScript[]; // Scripts to manage
  storageKey?: string; // Custom storage key
  googleAnalyticsId?: string; // GA4 tracking ID for auto-setup
}

CookiePreferences

interface CookiePreferences {
  functional: boolean; // Always true (required for site function)
  performance: boolean; // Analytics, monitoring, A/B testing
  advertising: boolean; // Marketing pixels, retargeting
  social: boolean; // Social media embeds, sharing
}

ConsentScript

interface ConsentScript {
  id: string; // Unique identifier
  category: ConsentCategory; // Which consent 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
}

Hook Return Type

interface ConsentManagerHook {
  preferences: CookiePreferences;
  updatePreferences: (preferences: CookiePreferences) => void;
  setCategoryConsent: (category: ConsentCategory, granted: boolean) => void;
  hasConsentedTo: (category: ConsentCategory) => boolean;
  hasConsentedOnce: () => boolean;
  acceptAll: () => void;
  rejectAll: () => void;
  resetConsent: () => void;
}

🔧 Configuration Examples

Google Analytics 4

const gaConfig: ConsentScript[] = [
  {
    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'
      gtag('consent', 'default', {
        analytics_storage: 'denied',
        ad_storage: 'denied',
        ad_user_data: 'denied',
        ad_personalization: 'denied'
      });
    `,
    vendor: "Google Analytics",
  },
];

Facebook Pixel

const facebookPixel: ConsentScript = {
  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",
};

Multiple Analytics Tools

const consentConfig: ConsentConfig = {
  debug: process.env.NODE_ENV === "development",
  defaults: {
    functional: true,
    performance: false,
    advertising: false,
    social: false,
  },
  scripts: [
    // Google Analytics
    ...gaConfig,

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

    // Facebook Pixel
    facebookPixel,

    // Twitter Pixel
    {
      id: "twitter-pixel",
      category: "advertising",
      consentRequired: true,
      strategy: "afterInteractive",
      content: `
        !function(e,t,n,s,u,a){e.twq||(s=e.twq=function(){s.exe?s.exe.apply(s,arguments):s.queue.push(arguments);
        },s.version='1.1',s.queue=[],u=t.createElement(n),u.async=!0,u.src='https://static.ads-twitter.com/uwt.js',
        a=t.getElementsByTagName(n)[0],a.parentNode.insertBefore(u,a))}(window,document,'script');
        twq('init','YOUR_TWITTER_PIXEL_ID');
        twq('track','PageView');
      `,
      vendor: "Twitter",
    },
  ],
};

💡 Usage Patterns

Programmatic Control

"use client";
import { useConsentManager } from "@consentry/next";
import { openConsentSettings } from "@consentry/ui";

export default function Footer() {
  const { hasConsentedOnce, acceptAll, rejectAll } = useConsentManager();

  return (
    <footer>
      <div>
        <button onClick={() => openConsentSettings()}>Cookie Settings</button>

        {!hasConsentedOnce && (
          <div>
            <button onClick={acceptAll}>Accept All Cookies</button>
            <button onClick={rejectAll}>Reject All</button>
          </div>
        )}
      </div>
    </footer>
  );
}

Conditional Rendering

"use client";
import { useConsentManager } from "@consentry/next";

export default function AnalyticsDashboard() {
  const { hasConsentedTo } = useConsentManager();

  if (!hasConsentedTo("performance")) {
    return (
      <div>
        <p>Analytics data requires performance cookies.</p>
        <button onClick={() => openConsentSettings()}>Enable Analytics</button>
      </div>
    );
  }

  return <div>{/* Your analytics dashboard */}</div>;
}

Social Media Embeds

"use client";
import { useConsentManager } from "@consentry/next";

export default function YouTubeEmbed({ videoId }: { videoId: string }) {
  const { hasConsentedTo, setCategoryConsent } = useConsentManager();

  if (!hasConsentedTo("social")) {
    return (
      <div className="consent-placeholder">
        <p>This content requires social media cookies.</p>
        <button onClick={() => setCategoryConsent("social", true)}>Enable Social Media</button>
      </div>
    );
  }

  return (
    <iframe
      src={`https://www.youtube.com/embed/${videoId}`}
      width="560"
      height="315"
      frameBorder="0"
      allowFullScreen
    />
  );
}

Custom Hook

import { useConsentManager } from "@consentry/next";
import { useCallback } from "react";

export function useAnalytics() {
  const { hasConsentedTo, setCategoryConsent } = useConsentManager();

  const trackEvent = useCallback(
    (event: string, data?: any) => {
      if (hasConsentedTo("performance") && typeof gtag !== "undefined") {
        gtag("event", event, data);
      }
    },
    [hasConsentedTo]
  );

  const enableAnalytics = useCallback(() => {
    setCategoryConsent("performance", true);
  }, [setCategoryConsent]);

  return {
    trackEvent,
    enableAnalytics,
    analyticsEnabled: hasConsentedTo("performance"),
  };
}

// Usage
function MyComponent() {
  const { trackEvent, analyticsEnabled } = useAnalytics();

  const handleClick = () => {
    trackEvent("button_click", { button_id: "header_cta" });
  };

  return <button onClick={handleClick}>Click me {analyticsEnabled && "(tracked)"}</button>;
}

🛠️ Advanced Configuration

Environment-Specific Setup

const getConsentConfig = (): ConsentConfig => {
  const isProd = process.env.NODE_ENV === "production";

  return {
    debug: !isProd,
    defaults: {
      functional: true,
      performance: isProd, // Auto-enable in production
      advertising: false,
      social: false,
    },
    scripts: isProd ? productionScripts : developmentScripts,
  };
};

Custom Storage Key

<ConsentManagerProvider config={consentConfig} storageKey="my-app-consent">
  {children}
</ConsentManagerProvider>

Server-Side Rendering Considerations

"use client";
import dynamic from "next/dynamic";

// Avoid hydration issues
const ConsentManager = dynamic(() => import("@consentry/ui"), {
  ssr: false,
});

const ConsentProvider = ({ children }: { children: React.ReactNode }) => {
  return (
    <ConsentManagerProvider config={consentConfig}>
      <ConsentManager mode="modal" />
      {children}
    </ConsentManagerProvider>
  );
};

🧪 Testing

Mock the Hook

// __mocks__/@consentry/next.ts
export const useConsentManager = () => ({
  preferences: {
    functional: true,
    performance: true,
    advertising: false,
    social: false,
  },
  updatePreferences: jest.fn(),
  setCategoryConsent: jest.fn(),
  hasConsentedTo: jest.fn(() => true),
  hasConsentedOnce: jest.fn(() => true),
  acceptAll: jest.fn(),
  rejectAll: jest.fn(),
  resetConsent: jest.fn(),
});

Test Components

import { render, screen } from "@testing-library/react";
import { useConsentManager } from "@consentry/next";
import MyComponent from "./MyComponent";

jest.mock("@consentry/next");

test("shows analytics when performance cookies enabled", () => {
  (useConsentManager as jest.Mock).mockReturnValue({
    hasConsentedTo: (category: string) => category === "performance",
  });

  render(<MyComponent />);
  expect(screen.getByText("Analytics Dashboard")).toBeInTheDocument();
});

🔒 Privacy & Compliance

GDPR Compliance

  • Consent before tracking — Scripts only load after explicit consent
  • Granular control — Users choose specific categories
  • Easy withdrawal — One-click preference changes
  • Data portability — Export/import consent settings

Google Consent Mode v2

The provider automatically handles Google's consent mode v2:

// Automatically called when preferences change
gtag("consent", "update", {
  analytics_storage: preferences.performance ? "granted" : "denied",
  ad_storage: preferences.advertising ? "granted" : "denied",
  ad_user_data: preferences.advertising ? "granted" : "denied",
  ad_personalization: preferences.advertising ? "granted" : "denied",
});

🚨 Common Issues & Solutions

Hydration Mismatch

// ❌ Wrong - causes hydration issues
export default function Layout({ children }) {
  return (
    <ConsentManagerProvider config={config}>
      <ConsentManager />
      {children}
    </ConsentManagerProvider>
  );
}

// ✅ Correct - use dynamic import
const ConsentManager = dynamic(() => import("@consentry/ui"), { ssr: false });

Scripts Not Loading

// ❌ Wrong - bypasses consent management
<Script src="https://analytics.example.com/script.js" />;

// ✅ Correct - managed by consent system
scripts: [
  {
    id: "analytics",
    category: "performance",
    src: "https://analytics.example.com/script.js",
  },
];

Hook Outside Provider

// ❌ Wrong - hook used outside provider
function App() {
  const { preferences } = useConsentManager(); // Error!
  return <div>...</div>;
}

// ✅ Correct - hook used inside provider
function App() {
  return (
    <ConsentManagerProvider config={config}>
      <MyComponent />
    </ConsentManagerProvider>
  );
}

function MyComponent() {
  const { preferences } = useConsentManager(); // Works!
  return <div>...</div>;
}

🔗 Related Packages

Complete Setup Guide

For the full experience with UI components, check out the @consentry/ui documentation which includes:

  • 🎨 Customizable cookie banners and modals
  • 🌈 Color themes and styling options
  • 🌍 Multi-language support
  • 📱 Mobile-optimized responsive design
  • ♿ Full accessibility support

📄 License

MIT — Copyright © 2025 Mustafa ONAL