Cookie Consent Banner

Generates a working consent banner - blocks tagged scripts until consent, Google Consent Mode v2, no dark patterns.

100% free. Runs entirely in your browser - your files and data never leave your device and nothing is uploaded to any server.

Template, not legal advice. This generates working code, not legal advice. It gives you a compliant mechanism; whether your site is compliant also depends on what you connect to it. Two things it cannot do for you: tag every third-party script so the blocker can hold it back, and keep a record of consents if your regulator expects one. Test it with your browser developer tools open - if a tracking cookie appears before you click Accept, something is still loading outside the blocker.

Start from an example

Fills every answer with a worked example you can edit. Pick the one closest to your business.

0 of 3 details filled
Your site
Which consent model

Opt-in is required in the UK and EU. If you have any European visitors at all, choose it.

Opt-in is required in the UK and EU. If you have any European visitors at all, choose it.
Appearance
Recommended. A single Accept-or-Reject is legal but converts worse than letting people keep analytics on and turn advertising off.
Signals and behaviour
Needed if you use Google Analytics or Google Ads and have EEA or UK visitors. Without it Google stops modelling data for those users.
Required in California and several other US states. The browser sends it automatically; this treats it as a rejection.
Consent has to be as easy to withdraw as it was to give, so there must be a way back to this after the banner is gone.

Cookie Consent Banner

5 sections · 1111 words · updates as you type

Cookie Consent Banner

Last updated: 12 November 2026

Paste this into your site

<!-- Cookie consent banner. Put the <style> and <script> in <head>,
     and the markup just before </body>. -->
<style>
#cc-banner{position:fixed;z-index:2147483000;right:16px;bottom:16px;max-width:420px;width:calc(100% - 32px);
  box-sizing:border-box;padding:20px;background:#fff;color:#0f172a;border:1px solid #e2e8f0;
  border-radius:14px;box-shadow:0 10px 40px rgba(2,6,23,.18);
  font:15px/1.55 system-ui,-apple-system,"Segoe UI",Roboto,sans-serif}
#cc-banner h2{margin:0 0 8px;font-size:1rem;font-weight:650}
#cc-banner p{margin:0 0 14px;color:#475569}
#cc-banner a{color:#4f46e5;text-decoration:underline}
#cc-actions{display:flex;gap:10px;flex-wrap:wrap}
#cc-banner button{flex:1 1 auto;min-width:120px;padding:11px 16px;border-radius:9px;
  font:inherit;font-weight:600;cursor:pointer;border:1px solid #4f46e5}
#cc-accept{background:#4f46e5;color:#fff}
#cc-reject{background:transparent;color:#4f46e5}
#cc-prefs{flex:0 0 auto;min-width:0;background:transparent;color:#475569;border-color:transparent;text-decoration:underline}
#cc-banner button:focus-visible{outline:3px solid #4f46e5;outline-offset:2px}
#cc-options{display:none;margin:0 0 14px;padding:12px 0 0;border-top:1px solid #e2e8f0}
#cc-options.cc-open{display:block}
#cc-options label{display:flex;gap:10px;align-items:flex-start;margin:0 0 10px;cursor:pointer}
#cc-options input{margin-top:3px;width:18px;height:18px;accent-color:#4f46e5}
#cc-options small{display:block;color:#64748b}
#cc-reopen{background:none;border:0;padding:0;font:inherit;color:inherit;text-decoration:underline;cursor:pointer}
@media (prefers-color-scheme:dark){
  #cc-banner{background:#0f172a;color:#e2e8f0;border-color:#1e293b}
  #cc-banner p{color:#94a3b8}
  #cc-prefs{color:#94a3b8}
  #cc-options{border-top-color:#1e293b}#cc-options small{color:#94a3b8}
}
@media (prefers-reduced-motion:reduce){#cc-banner{transition:none}}
</style>

<div id="cc-banner" role="dialog" aria-modal="false" aria-labelledby="cc-title" aria-describedby="cc-desc" hidden>
  <h2 id="cc-title">Cookies on this site</h2>
  <p id="cc-desc">We use cookies to run this site and, with your permission, to understand how it is used.
    Nothing optional is set until you choose.
    <a href="/cookie-policy">Read our cookie policy</a>.</p>
  <div id="cc-options">
    <label><input type="checkbox" checked disabled>
      <span>Essential<small>Needed for the site to work. Always on.</small></span></label>
    <label><input type="checkbox" id="cc-analytics">
      <span>Analytics<small>Counts visits so we can see what is used.</small></span></label>
    <label><input type="checkbox" id="cc-marketing">
      <span>Marketing<small>Measures advertising and allows targeted ads.</small></span></label>
  </div>
  <div id="cc-actions">
    <button type="button" id="cc-accept">Accept all</button>
    <button type="button" id="cc-reject">Reject all</button>
    <button type="button" id="cc-prefs">Choose</button>
  </div>
</div>

<script>
(function () {
  var KEY = "cc-consent-v1";
  var el = function (id) { return document.getElementById(id); };
  var banner = el("cc-banner");

  function read() {
    try { return JSON.parse(localStorage.getItem(KEY) || "null"); } catch (e) { return null; }
  }
  function write(state) {
    try { localStorage.setItem(KEY, JSON.stringify(state)); } catch (e) {}
  }

  // Google Consent Mode v2. Must run BEFORE the Google tag loads,
  // which is why this snippet goes in the head.
  window.dataLayer = window.dataLayer || [];
  function gtag() { window.dataLayer.push(arguments); }
  function signal(state) {
    gtag("consent", "update", {
      analytics_storage: state.analytics ? "granted" : "denied",
      ad_storage: state.marketing ? "granted" : "denied",
      ad_user_data: state.marketing ? "granted" : "denied",
      ad_personalization: state.marketing ? "granted" : "denied"
    });
  }
  gtag("consent", "default", {
    analytics_storage: "denied",
    ad_storage: "denied",
    ad_user_data: "denied",
    ad_personalization: "denied",
    wait_for_update: 500
  });

  // Turns every blocked script tag of a granted category into a real one.
  // Scripts are written as: <script type="text/plain" data-consent="analytics" src="...">
  function unblock(state) {
    var nodes = document.querySelectorAll("script[type='text/plain'][data-consent]");
    for (var i = 0; i < nodes.length; i++) {
      var node = nodes[i];
      var want = node.getAttribute("data-consent");
      if (!state[want]) continue;
      var live = document.createElement("script");
      for (var j = 0; j < node.attributes.length; j++) {
        var at = node.attributes[j];
        if (at.name !== "type" && at.name !== "data-consent") live.setAttribute(at.name, at.value);
      }
      live.text = node.text;
      node.parentNode.replaceChild(live, node);
    }
  }

  function apply(state) {
    write(state);
    signal(state);
    unblock(state);
    document.dispatchEvent(new CustomEvent("cc:consent", { detail: state }));
    hide();
  }

  function show() {
    banner.hidden = false;
    banner.querySelector("button").focus();
  }
  function hide() {
    banner.hidden = true;
  }

  el("cc-accept").addEventListener("click", function () {
    apply({ essential: true, analytics: true, marketing: true, at: Date.now() });
  });
  el("cc-reject").addEventListener("click", function () {
    apply({ essential: true, analytics: false, marketing: false, at: Date.now() });
  });
  el("cc-prefs").addEventListener("click", function () {
    var panel = el("cc-options");
    if (!panel.classList.contains("cc-open")) {
      panel.classList.add("cc-open");
      this.textContent = "Save choices";
      return;
    }
    apply({
      essential: true,
      analytics: el("cc-analytics").checked,
      marketing: el("cc-marketing").checked,
      at: Date.now()
    });
  });

  // Withdrawing consent has to be as easy as giving it, so this
  // reopens the banner from anywhere: <button id="cc-reopen">Cookie settings</button>
  var reopen = el("cc-reopen");
  if (reopen) reopen.addEventListener("click", show);

  var saved = read();
  // Global Privacy Control: a browser-level opt-out that US state law
  // requires you to honour. Treated as a rejection, recorded so the
  // banner does not then ask a question it has already answered.
  if (!saved && (navigator.globalPrivacyControl === true)) {
    apply({ essential: true, analytics: false, marketing: false, gpc: true, at: Date.now() });
    return;
  }
  if (saved) {
    signal(saved);
    unblock(saved);
    if (el("cc-analytics")) el("cc-analytics").checked = !!saved.analytics;
    if (el("cc-marketing")) el("cc-marketing").checked = !!saved.marketing;
  } else {
    show();
  }
})();
</script>

How to install it

1. Copy the block above into your page. The style and script can go in the head; the markup goes just before the closing body tag. On WordPress, a "header and footer scripts" plugin is the usual place; on Shopify it is theme.liquid.

2. Find every tracking script on your site - analytics, pixels, chat widgets, heatmaps.

3. Change each one from type="text/javascript" to type="text/plain" and add data-consent="analytics" or data-consent="marketing". That is what holds them back until someone agrees; a script you forget to tag will keep loading regardless of what the banner says.

4. Leave the Google tag itself in place and untagged. Consent Mode signals handle it: the tag loads, but it stores nothing until the consent update grants it.

5. Add a button with id="cc-reopen" to your footer, labelled something like "Cookie settings". Consent must be as easy to withdraw as it was to give, and that button is how.

6. Test it. Open a private window with developer tools on the Application tab, load the site, and look at Cookies before you click anything. If a tracker has already written one, it is not tagged properly.

What a blocked script looks like

<!-- BEFORE - runs immediately, sets cookies before anyone agrees -->
<script src="https://example-analytics.com/tag.js"></script>

<!-- AFTER - held back until consent for its category exists -->
<script type="text/plain" data-consent="analytics"
        src="https://example-analytics.com/tag.js"></script>

<!-- Inline scripts work the same way -->
<script type="text/plain" data-consent="marketing">
  /* pixel code here */
</script>

Reacting to a choice in your own code

// Fires every time a decision is made or restored on load.
document.addEventListener("cc:consent", function (e) {
  if (e.detail.analytics) {
    // start something that needs analytics consent
  }
});

// Read the stored decision anywhere:
var consent = JSON.parse(localStorage.getItem("cc-consent-v1") || "null");

What this banner does and does not do

What it does

  • Sets nothing before a choice. Tagged scripts stay inert until someone accepts, which is what the UK and EU rules require and what most free snippets get wrong.
  • Gives Reject the same weight as Accept. Same size, same prominence, one click. A greyed-out or hidden reject option is a dark pattern that has attracted fines.
  • Remembers the decision in localStorage under a versioned key, so changing the key re-asks everyone when your cookies change.
  • Works with a keyboard and a screen reader. It is a labelled dialog, focus moves into it, and the controls are real buttons.
  • Honours Global Privacy Control. Browsers that send the signal are treated as having rejected, without being asked - which is what California and several other states require.
  • Sends Google Consent Mode v2 signals , so Google Analytics and Google Ads behave correctly for EEA and UK visitors instead of silently dropping them.

What it does not do

  • It does not keep a server-side record of who consented and when. If your regulator or your DPO expects an audit trail, you need to post the decision to your own endpoint - the cc:consent event is where to do it.
  • It does not scan your site for trackers. Anything you do not tag keeps loading.
  • It does not write your cookie policy. The banner links to it; you still need the page.

Generated from a template. Not legal advice - read it before you publish or sign it.

Other documents you can generate

Most sites need more than one. A privacy policy without terms, or terms without a refund policy, leaves the obvious gap.

Privacy PolicyA privacy policy written from what your site actually does - GDPR and CCPA sections included only where they apply.Terms and ConditionsTerms of service built around what you actually sell - goods, subscription, downloads or services.Cookie PolicyA cookie policy with a real table - each cookie named, with its purpose and how long it lasts.Refund and Return PolicyA refund policy customers can follow and payment processors accept - windows, exclusions and statutory rights in plain words.Website DisclaimerA disclaimer that names the actual risk your site carries, plus affiliate and sponsorship disclosures.Accessibility StatementA WCAG accessibility statement that states what you actually meet, names known issues, and gives people a route to report problems.Non-Disclosure AgreementA mutual or one-way NDA with a real purpose clause, standard carve-outs and a defined term.Freelance ContractAn independent contractor agreement that pins down scope, revisions, IP ownership and what happens when payment is late.Data Processing AgreementA GDPR Article 28 processor agreement with the eight mandatory clauses and a filled-in processing schedule.W-9 FormFills a substitute Form W-9 in your browser - your TIN is never uploaded, stored or sent anywhere.1099-NEC Contractor SummaryPrepares a 1099-NEC recipient statement and a payer summary - with a straight answer about what you can and cannot print yourself.Shipping PolicyA shipping policy Shopify and Amazon sellers can publish - origin, times, tracking, duties and who pays.DSAR Form GeneratorA data-delete and access request page you can host - GDPR and CCPA wording, no upload, no signup.

About this tool

A cookie banner is the part of cookie compliance that actually does something: it stops non-essential scripts from running until a visitor agrees. Most free snippets get this backwards - they set the cookie first and show a notice second, which is exactly what the ePrivacy rules forbid. This generator outputs copy-and-paste HTML, CSS and JavaScript with Accept and Reject buttons of equal prominence and category toggles off by default, and it holds back tagged scripts until consent is given. Add it the same day you add analytics or an advertising pixel. It needs no external service or monthly fee, and your settings stay in your browser.

How to use this tool

  1. Set up the bannerEnter your site name, the link to your cookie policy and the consent model you need.
  2. Choose the categoriesPick the analytics and marketing categories to offer, all off by default.
  3. Install and retag scriptsPaste the code into your site and retag each tracking script so the banner can hold it back until consent.

What makes a consent banner compliant

UK and EU regulators have been consistent about a few points: non-essential cookies must not load before the visitor agrees, rejecting must be as easy as accepting, pre-ticked boxes do not count as consent, and people must be able to change their mind later. A banner with only an "OK" button, or a reject option hidden two screens deep, does not meet that standard.

The banner this page builds holds back tagged scripts until the visitor chooses, offers Accept and Reject with equal weight and stores the choice so it is not asked on every page. What it cannot do is block scripts you have not tagged - so testing with the browser developer tools open is part of the job.

Key features

  • Accept and Reject buttons with equal prominence, as UK and EU guidance expects.
  • Category toggles for analytics and marketing, off by default.
  • Holds back tagged scripts until consent, then loads only the categories chosen.
  • Copy-and-paste HTML, CSS and JavaScript with no external service or monthly fee.

Common uses

  • A small business site with Google Analytics and UK or EU visitors.
  • A shop running Meta or Google Ads remarketing tags.
  • Replacing a paid consent platform on a site with only a few tags.
  • A developer who needs a lightweight banner for a client project.

Tips for better results

  • Test with developer tools open: no tracking cookie should appear before you click Accept.
  • Give Reject the same size and colour as Accept - UK and EU guidance expects an equal choice.
  • Link the banner to a cookie policy that uses the same category names.

Mistakes to avoid

  • Installing the banner but leaving tracking scripts untagged, so pixels keep firing before consent.
  • Hiding Reject behind a settings screen while Accept sits on the first layer.
  • Forgetting to add new scripts to the banner when you install another tool later.

Frequently asked questions

US law generally does not require opt-in consent for cookies, though some state privacy laws require an opt-out for selling or sharing data for targeted ads. If you get UK or EU traffic, an opt-in banner is the safer choice.

Change the script type to text/plain and add a data attribute naming its category; the banner switches it back and runs it after consent. Inline scripts and tags loaded through a tag manager need the same treatment.

Yes - visitors who reject are not counted. Google Consent Mode can model some of the missing data, but expect lower numbers than before the banner.

No. It assembles a draft from standard clauses, and every page says so above the preview. The largest EU cookie fines have not been for missing banners. They were for banners where rejecting was harder than accepting, or where trackers ran regardless of the answer.

Broadly, developers and site owners who need a working consent mechanism rather than another policy page. The best time to do it is the same day you add analytics or an advertising pixel, not afterwards.

Installing the banner and leaving the tracking scripts untouched. The banner blocks nothing on its own - every script has to be retagged so the blocker can hold it back, and an untagged pixel keeps firing while the banner politely asks permission.

Most people need Cookie Policy and Privacy Policy as well. They cover the gaps this one does not.

Related guides