Socket
Book a DemoInstallSign in
Socket

@consentry/next

Package Overview
Dependencies
Maintainers
1
Versions
21
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@consentry/next

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

latest
Source
npmnpm
Version
0.1.21
Version published
Maintainers
1
Created
Source

@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

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.

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

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

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

Keywords

nextjs

FAQs

Package last updated on 19 Jun 2025

Did you know?

Socket

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.

Install

Related posts