Paste the TermsFeed Free Cookie Consent code into the base layout of your static site generator, and every page you build carries the cookie banner. Cookie Consent runs entirely in the browser: it needs no server of your own, no plugin, and no build step of its own. Cookie Consent stores the answer from each visitor in 3 first-party cookies and, by default, holds each optional script you tag until the visitor accepts its category.

Static site generators do not ship a consent banner of their own, so there is nothing to turn off. A theme that reads a measurement ID from the site configuration prints Google Analytics 4 by itself, and that theme output runs before any consent. Setup takes 4 steps and about 20 minutes.

You need the code from the TermsFeed generator at https://www.termsfeed.com/cookie-consent/ before you start. Create it with Install a Cookie Banner on Any Website.

Put the generator code before the closing </body> tag of the layout that wraps every page, or anywhere else in that layout. The generator wraps cookieconsent.run() in a DOMContentLoaded listener, which waits until the whole page is parsed. Cookie Consent reads the tagged scripts once, when it starts, so it finds every tagged script in the page, above or below the generator code.

Paste your own copy of the generator block in place of this example, because your copy carries the options you chose.

<!-- Cookie Consent by TermsFeed https://www.TermsFeed.com -->
<script type="text/javascript" src="https://www.termsfeed.com/public/cookie-consent/4.2.0/cookie-consent.js" charset="UTF-8"></script>
<script type="text/javascript" charset="UTF-8">
document.addEventListener('DOMContentLoaded', function () {
cookieconsent.run({"notice_banner_type":"headline","consent_type":"express","palette":"dark","language":"en","page_load_consent_levels":["strictly-necessary"],"notice_banner_reject_button_hide":false,"preferences_center_close_button_hide":false,"page_refresh_confirmation_buttons":false,"website_name":"Example Site"});
});
</script>

<noscript>Free cookie consent management tool by <a href="https://www.termsfeed.com/">TermsFeed Generator</a></noscript>
<!-- End Cookie Consent by TermsFeed https://www.TermsFeed.com -->

Hugo, Jekyll, and Eleventy keep the closing </body> tag in a layout file, while Gatsby builds the page from code in gatsby-ssr.js and Docusaurus from configuration.

Generator File that holds the closing body tag Theme analytics to replace
Hugo layouts/baseof.html in Hugo 0.146 and later, layouts/_default/baseof.html in earlier versions (copy it from the theme into your project to override it) {{ partial "google_analytics.html" . }}, printed when services.googleAnalytics.id is set
Jekyll with Minima _layouts/base.html in Minima 3, or _layouts/default.html in Minima 2.5, the version jekyll new installs (copy it from the theme into your project to override it) google-analytics.html, included in production builds when google_analytics is set in _config.yml
Eleventy The base layout of your project, such as _includes/layouts/base.njk Any analytics include your starter project adds

Gatsby writes the page shell from gatsby-ssr.js, so add the Cookie Consent block with the onRenderBody API. setPostBodyComponents places elements before the closing </body> tag, outside the React tree that Gatsby hydrates. Gatsby renders these elements only into the HTML it builds, which is where the script expects them.

Add the 2 script elements and the credits line to gatsby-ssr.js. Replace the configuration object with the one the generator gave you.

// gatsby-ssr.js
const React = require("react")

const cookieConsentConfig = {
  notice_banner_type: "headline",
  consent_type: "express",
  palette: "dark",
  language: "en",
  page_load_consent_levels: ["strictly-necessary"],
  website_name: "Example Site",
}

exports.onRenderBody = ({ setPostBodyComponents }) => {
  setPostBodyComponents([
    <script
      key="cookie-consent-loader"
      type="text/javascript"
      src="https://www.termsfeed.com/public/cookie-consent/4.2.0/cookie-consent.js"
      charSet="UTF-8"
    />,
    <script
      key="cookie-consent-run"
      type="text/javascript"
      charSet="UTF-8"
      dangerouslySetInnerHTML={{
        __html: `document.addEventListener('DOMContentLoaded', function () { cookieconsent.run(${JSON.stringify(cookieConsentConfig)}); });`,
      }}
    />,
    <noscript key="cookie-consent-credits">
      Free cookie consent management tool by <a href="https://www.termsfeed.com/">TermsFeed Generator</a>
    </noscript>,
  ])
}

Docusaurus puts external scripts in the <head> through the scripts option of docusaurus.config.js, and runs your own code through a client module. The loader in the head defines cookieconsent before any page code runs. The client module then calls run() once the page is parsed.

Add the loader and the client module to docusaurus.config.js. Docusaurus 3 reads both keys at the root of the configuration.

// docusaurus.config.js (excerpt)
export default {
  // ...your existing configuration
  scripts: [
    {
      src: 'https://www.termsfeed.com/public/cookie-consent/4.2.0/cookie-consent.js',
      charset: 'UTF-8',
    },
  ],
  clientModules: ['./src/cookie-consent.js'],
};

Create src/cookie-consent.js with the configuration from the generator. Docusaurus runs client modules during the static build as well, so the code checks for a browser first.

// src/cookie-consent.js
import ExecutionEnvironment from '@docusaurus/ExecutionEnvironment';

if (ExecutionEnvironment.canUseDOM) {
  const startCookieConsent = function () {
    window.cookieconsent.run({
      notice_banner_type: 'headline',
      consent_type: 'express',
      palette: 'dark',
      language: 'en',
      page_load_consent_levels: ['strictly-necessary'],
      website_name: 'Example Site',
    });
  };

  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', startCookieConsent);
  } else {
    startCookieConsent();
  }
}

Step 2: Turn off the analytics your theme prints

Turn off the analytics your theme or plugin prints. Then add each script again as a tagged copy, which Cookie Consent runs once the visitor accepts its category. Find the tagged Google Analytics 4 tag in Block Google Analytics 4 Until Consent, and every other tool in Vendor Recipes for Free Cookie Consent.

The place to turn off the theme analytics and to add the tagged copy differs for each generator.

  • Hugo. Set privacy.googleAnalytics.disable to true in the Hugo site configuration, so the embedded google_analytics.html partial prints nothing. Put the tagged copy in a partial of your own, called from the <head> of the base template.
  • Jekyll with Minima. Remove the google_analytics key from _config.yml, and paste the tagged copy into _includes/custom-head.html in Minima 3, or into a copy of _includes/head.html in Minima 2.5. Minima includes that file in the <head> of every page.
  • Eleventy. Edit the include that holds the script, because Eleventy prints only what your templates hold.
  • Gatsby. Take gatsby-plugin-google-gtag out of gatsby-config.js, add setHeadComponents to the parameters of the onRenderBody function in gatsby-ssr.js, and pass it the tagged copy.
  • Docusaurus. Remove the gtag option of the classic preset, or @docusaurus/plugin-google-gtag, and add the tagged copy with the headTags option of docusaurus.config.js.

Move the "Update cookies preferences" link from the end of the generator block into the footer partial, so visitors reopen the Preferences Center from every page you build. The link carries the id open_preferences_center. Once Cookie Consent runs, a click on it opens the Preferences Center, and Cookie Consent keeps the # address from scrolling the page to the top.

<a href="#" id="open_preferences_center">Update cookies preferences</a>

Docusaurus builds its footer from themeConfig.footer.links, where an item with an html key holds raw markup. Put the open_preferences_center link in such an item. For a menu item whose editor has no id field, open the Preferences Center with a class selector from Add a Cookie Settings Link.

The checks assume express consent with page_load_consent_levels left out or at the generator value ["strictly-necessary"]. Test the built site, because a development server sometimes leaves out production-only output such as the Jekyll analytics include. Build the site and deploy it to your live hostname, or serve the output folder from a build without cookie_domain. Close every private window, open a new one, and run these checks there.

  1. Load the site, open Chrome DevTools, select the Network tab, type cookie-consent in the filter box, and reload. The loader cookie-consent.js, the script cookie-consent-code.js, and the ping to www.termsfeed.com appear, and the consent banner shows.
  2. Change the filter to the host of one tagged script and reload. No request appears before you answer.
  3. Click the "I agree" button. Requests to that host appear without a reload.
  4. Open the Application tab, select Cookies and your site. cookie_consent_level, cookie_consent_user_accepted, and cookie_consent_user_consent_token are listed.
  5. Click the footer link, and select the tab of the category you tagged the script with, such as "Tracking cookies", in the Preferences Center that opens. The switch on that tab reads "Active".

A pass on every check confirms that the base layout and the tagged analytics work together. Run the remaining checks with Check That Your Cookie Banner Works.

Cookie Consent writes host-only cookies unless you set cookie_domain, so every hostname keeps its own answer. A deploy preview on Netlify, Vercel, or Cloudflare Pages runs on a different hostname from your production site. The consent banner appears again on each preview, which is expected and matches what a new visitor sees.

Set cookie_domain only in the production build. Browsers refuse a cookie for a domain outside the page host. A production cookie_domain on a preview hostname does not store an answer, and the consent banner returns on every page. Hugo exposes the environment as hugo.Environment, and Jekyll as jekyll.environment, which lets the base layout print the option only for production. Share one answer across your own subdomains with Share Consent Across Subdomains.

Check the built output rather than the preview for each of these, because a theme, a minifier, or a build step changes the code between the preview and the build.

  • The theme still prints Google Analytics. A measurement ID left in the site configuration or a plugin left in the build adds an untagged copy. Search the built HTML for googletagmanager.com and confirm every <script tag among the matches carries type="text/plain". Turn off the theme output with the setting for your generator in Turn off the analytics your theme prints.
  • The tagged script sits in a page template, not in the base layout. The script then runs only on the pages built from that template. Search the built HTML of a page from another template for the tracker host, and no match points to this mistake. Put shared scripts in the base layout.
  • The block is pasted twice. A partial included by 2 layouts adds the block twice, and the built HTML of a page holds 2 cookie-consent.js script tags. The second run() call does nothing, and its options are ignored, so a change made only in that copy has no effect. Keep one copy per page.
  • A minifier rewrites the tagged scripts. An HTML minifier that drops type="text/plain" or the data-cookie-consent attribute makes the script run at once. Check the attributes in the built output, and turn off the minifier option that removes them.
  • A Gatsby or Docusaurus page loads more code after the first page. Both generators switch pages in the browser without a reload, and tagged scripts added by a later page never run. The Network tab does not show any request from such a script, even after you accept. Follow the route rules in Single-Page Apps and Frameworks.

A consent banner missing from the deployed build has its own list of causes. Fix it with Cookie Banner Not Appearing.