GDPR Compliant Cookie Banner: 2KB CDN Solution for Any Website

Library: smallest-cookie-banner | Size: ~2KB gzipped | License: MIT

Every website that uses analytics, advertising, or tracking needs user consent in the EU under GDPR. Most cookie consent solutions are bloated - OneTrust clocks in at 100KB+, and even simpler solutions like cookieconsent hit 25KB. That's larger than many entire websites need to be.

When you're building a fast, performant site, adding a 100KB consent manager defeats the purpose. You need something that:

  • Loads instantly (no impact on Core Web Vitals)
  • Works without a build step (CDN-loadable)
  • Handles GDPR compliance correctly
  • Doesn't require a SaaS subscription

smallest-cookie-banner is a ~2KB gzipped library that handles cookie consent properly. It's MIT licensed, has zero dependencies, and loads from unpkg CDN.

Key features:

  • Geo-aware: Auto-detects EU users via timezone for GDPR
  • Implied consent: Auto-accepts in regions where it's legal (USA, Asia)
  • WCAG 2.1 AA accessible: Keyboard navigation, screen readers, 44px touch targets
  • Fully customizable: Every string, style, and behavior can be configured

Quick Installation (CDN)

The simplest setup - just two script tags:

<!-- Set config before loading -->
<script>
  window.CookieBannerConfig = {
    msg: 'We use cookies for analytics.',
    acceptText: 'Accept',
    rejectText: 'Decline',
    onAccept: function() {
      // Load your analytics here
      console.log('User accepted cookies');
    },
    onReject: function() {
      // Disable tracking
      console.log('User rejected cookies');
    }
  };
</script>

<!-- Load the library -->
<script src="https://cdn.jsdelivr.net/npm/smallest-cookie-banner@2.1.2/dist/cookie-banner.min.js"></script>

That's it. The banner appears for EU users with Accept/Decline buttons. Non-EU users get a simpler "OK" button with implied consent.

Configuration Options

window.CookieBannerConfig = {
  // Text (fully customizable for i18n)
  msg: 'We use cookies to enhance your experience.',
  acceptText: 'Accept',
  rejectText: 'Decline',

  // Behavior
  days: 365,              // Cookie expiry
  forceEU: true,          // Force EU mode (show Accept/Decline everywhere)
  autoAcceptDelay: 5000,  // Auto-accept after 5s for non-EU (0 to disable)
  cookieName: 'ck',       // Custom cookie name
  cookieDomain: '.example.com', // For subdomains

  // Callbacks
  onAccept: function() { /* Load analytics */ },
  onReject: function() { /* Disable tracking */ },

  // Styling
  css: '#ckb { background: #333; }',  // Additional CSS
  style: 'border-radius: 8px;'        // Inline styles
};

Toast-Style Corner Banner

Want a modern toast notification instead of a full-width bar? Add custom CSS:

window.CookieBannerConfig = {
  msg: 'We use cookies for analytics.',
  acceptText: 'Accept',
  rejectText: 'Decline',
  forceEU: true,
  css: `
    #ckb {
      bottom: 20px !important;
      right: 20px !important;
      left: auto !important;
      width: 320px;
      border-radius: 12px;
      flex-direction: column;
      text-align: center;
      box-shadow: 0 4px 20px rgba(0,0,0,0.3);
    }
    #ckb p { min-width: auto; }
  `
};

Integrating with Google Analytics

The key to GDPR compliance is only loading tracking scripts AFTER consent:

window.CookieBannerConfig = {
  msg: 'We use cookies for analytics.',
  acceptText: 'Accept',
  rejectText: 'Decline',
  forceEU: true,
  onAccept: function() {
    // Only load GA after user accepts
    if (!document.querySelector('script[src*="googletagmanager.com/gtag/js"]')) {
      var s = document.createElement('script');
      s.async = true;
      s.src = 'https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX';
      document.head.appendChild(s);

      window.dataLayer = window.dataLayer || [];
      function gtag(){dataLayer.push(arguments);}
      window.gtag = gtag;
      gtag('js', new Date());
      gtag('config', 'G-XXXXXXXXXX');
    }
  },
  onReject: function() {
    // Disable GA if user rejects
    window['ga-disable-G-XXXXXXXXXX'] = true;
  }
};

Gatsby/React Integration

For Gatsby sites, add the banner via gatsby-ssr.js:

const React = require("react");

exports.onRenderBody = ({ setPostBodyComponents }) => {
  setPostBodyComponents([
    // Config must come before script
    <script
      key="cookie-banner-config"
      dangerouslySetInnerHTML={{
        __html: `
          window.CookieBannerConfig = {
            msg: 'We use cookies for analytics.',
            acceptText: 'Accept',
            rejectText: 'Decline',
            forceEU: true,
            onAccept: function() {
              // Load GA
            },
            onReject: function() {
              window['ga-disable-G-XXXXXXXXXX'] = true;
            }
          };
        `,
      }}
    />,
    <script
      key="cookie-banner-script"
      src="https://cdn.jsdelivr.net/npm/smallest-cookie-banner@2.1.2/dist/cookie-banner.min.js"
    />,
  ]);
};

Important: Set the config BEFORE loading the script. The library auto-initializes when it detects window.CookieBannerConfig.

Common Gotchas

Double Banner Issue

If you see two banners, you're likely:

  1. Setting config AND calling createCookieBanner() manually
  2. Loading the script twice

The library auto-initializes when config exists. Don't call createCookieBanner() unless you're not setting window.CookieBannerConfig.

If the banner doesn't appear:

  1. Check if a ck cookie already exists (user already consented)
  2. Non-EU users get auto-accepted silently - use forceEU: true to test
  3. Clear cookies: document.cookie = "ck=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;"
// After banner loads, check consent:
CookieBanner.ok    // true (accepted), false (rejected), null (no decision)

// Programmatic control:
CookieBanner.yes()    // Accept
CookieBanner.no()     // Reject
CookieBanner.reset()  // Clear and reload page

npm Installation (Alternative)

If you prefer npm over CDN:

npm install smallest-cookie-banner
import 'smallest-cookie-banner';

// Set config in useEffect (React) or mounted (Vue)
window.CookieBannerConfig = {
  msg: 'We use cookies.',
  onAccept: () => loadAnalytics()
};

Size Comparison

Library Size
smallest-cookie-banner ~2KB
cookie-consent ~15KB
cookieconsent ~25KB
tarteaucitron ~45KB
OneTrust ~100KB+

The library handles these regulations:

Region Law Behavior
EU GDPR Shows Accept + Reject buttons
UK UK GDPR Shows Accept + Reject buttons
California CCPA Shows OK button (implied consent)
Brazil LGPD Shows Accept + Reject buttons
Rest of World Various Shows OK button (implied consent)

The geo-detection uses timezone offset, which is reliable for determining EU vs non-EU without any external API calls.

Conclusion

smallest-cookie-banner solves cookie consent without the bloat. At 2KB, it's smaller than most favicons. It loads from CDN, requires no build step, and handles GDPR compliance correctly.

For most sites, the CDN approach shown here is the simplest path to compliance. Set your config, load the script, and you're done.

Resources:

Fred

Fred

AUTHOR

Full-stack developer with 10+ years building production applications. This blog is built with Gatsby - I know the framework inside and out.

Need a developer who gets it?

POC builds, vibe-coded fixes, and real engineering. Let's talk.

Hire Me →