Platforms

How to add a cookie consent banner to a Gatsby site

In Gatsby, add the snippet to the head of every page from gatsby-ssr.js with onRenderBody and setHeadComponents, so it is in the static HTML before Gatsby's own scripts. Mark analytics and advertising tags in the same place with type="text/plain" and data-cq-category, and send route-change page views from gatsby-browser.js only when the category is granted.
Published 21 October 2026 · By the Cookiequiet team · Markdown

Steps

Copy the snippetIn the Cookiequiet dashboard, add the website and copy the snippet. It carries your site key and the data-consent-mode and data-auto-block attributes.
Add it in gatsby-ssr.jsCreate or open gatsby-ssr.js at the project root, export onRenderBody and call setHeadComponents with a script element for cq.js, with a key and the data attributes from the snippet.
Put it first in the headIf plugins add their own head tags, export onPreRenderHTML and move the Cookiequiet element to the front with getHeadComponents and replaceHeadComponents.
Mark your tagsAdd analytics or advertising tags in the same onRenderBody call as script elements with type="text/plain" and data-cq-category, or replace plugins that print a tracker into the HTML with marked tags.
Gate route-change trackingIn gatsby-browser.js, send page views from onRouteUpdate only when the cq:consent event has granted the category.
Add the Cookie preferences linkIn the footer component, render a plain anchor with href="#cq-preferences" and the data-cq-show-preferences attribute.
Build and testRun gatsby build and gatsby serve, open the site in a private window, click Reject all and check that no _ga or _fbp cookie is set. Accept all, reload and check the consent log.

Where does the snippet go in Gatsby?

Gatsby renders every page to static HTML at build time. The server rendering APIs in gatsby-ssr.js let you add elements to that HTML: onRenderBody receives setHeadComponents, which adds components to the head of every page. That is where the snippet belongs, because it is then in the HTML before Gatsby's JavaScript bundles.

jsx
// gatsby-ssr.js
import * as React from "react";
​
export const onRenderBody = ({ setHeadComponents }) => {
setHeadComponents([
<script
key="cookiequiet"
src="https://cookiequiet.com/cq.js"
data-site="YOUR_SITE_KEY"
data-consent-mode="1"
data-auto-block="1"
defer
/>,
]);
};

Do not use Gatsby's <Script> component for this. Its strategies (post-hydrate, idle and off-main-thread) all load after hydration, which is too late for a script that has to run before the tags it controls.

Plugins that add head tags. If a plugin also calls setHeadComponents, export onPreRenderHTML to put the snippet first. Gatsby documents this hook for reordering what other plugins added:

jsx
export const onPreRenderHTML = ({ getHeadComponents, replaceHeadComponents }) => {
const head = getHeadComponents();
head.sort((a, b) => (a && a.key === "cookiequiet" ? -1 : b && b.key === "cookiequiet" ? 1 : 0));
replaceHeadComponents(head);
};

When several plugins implement onPreRenderHTML, the last one wins, so check the built HTML.

Marked tags in gatsby-ssr.js. Add them in the same setHeadComponents array, after the snippet. Cookiequiet turns them into running scripts when the category is granted, and at once on later visits when the stored choice includes it:

jsx
<script
key="ga4-config"
type="text/plain"
data-cq-category="analytics"
dangerouslySetInnerHTML={{ __html: "/* your Google tag configuration, unchanged */" }}
/>

The external gtag.js script gets the same type and data-cq-category. The Google Analytics 4 guide shows both tags and the Consent Mode alternative.

Plugins that print a tracker. A plugin that writes a tracking script into the HTML at build time runs before any consent script can stop it, unless it is a Google tag loading under Consent Mode with denied defaults. For other trackers, remove the plugin and add the marked tag yourself. Scripts that are created later by JavaScript (a tag manager, a chat widget) are held by the auto-blocker when they come from one of the about 80 trackers it knows.

What changes with client-side navigation?

After the first load, Gatsby navigates without reloading the page. The snippet runs once per full load and the choice stays applied, so the banner does not reappear. Two consequences:

  • Marked scripts are activated when a choice is applied on load and after each choice. Keep them in gatsby-ssr.js, not inside page components.
  • Page views on route changes are your code's job. Gatsby calls onRouteUpdate in gatsby-browser.js on every route change, including the initial load. Send the page view only when analytics is granted.

Cookiequiet dispatches a cq:consent event on window whenever it applies a choice (a stored one on load, a new one after a click, or every category in regions where the banner is not shown). Its detail.categories lists the granted categories:

js
// gatsby-browser.js
let analytics = false;
window.addEventListener("cq:consent", (e) => {
analytics = e.detail.categories.indexOf("analytics") >= 0;
});
​
export const onRouteUpdate = ({ location }) => {
if (!analytics) return;
// send your page view for location.pathname here
};

window.Cookiequiet.get() returns the choice in force (its c array lists the categories): the stored one, or a: "geo" with every category where geo-targeting shows no banner. It returns null while no choice applies yet.

Render a plain anchor in the footer component. A hash link does not need Gatsby's <Link>:

jsx
<a href="#cq-preferences" data-cq-show-preferences>Cookie preferences</a>

How do I test it?

  1. Run gatsby build then gatsby serve: gatsby-ssr.js only affects the built HTML, so test the build rather than gatsby develop.
  2. View the page source: the Cookiequiet script must be the first script in the head.
  3. Open the site in a private window and click Reject all. The cookies should be cq_consent and nothing from Google or Meta.
  4. Clear the cookies, click Accept all, reload: the marked scripts run. Navigate between pages and check that the choice holds.
  5. Open the consent log in the dashboard.

The cookie scanner lists what the deployed site sets before consent. Cookiequiet is free for one website with geo-targeting included, and Starter is €3 per website per month with no pageview limit.

What else is specific to Gatsby?

  • Content Security Policy. Allow https://cookiequiet.com in script-src and connect-src: the banner fetches its configuration, the geo lookup and the consent records from that origin.
  • Partytown. Keep cq.js on the main thread. It reads the document and controls the scripts on it.
  • Static hosting. The banner needs no server code, so Netlify, Cloudflare Pages or any static host works.

Questions

Not as the consent script. The Script component's strategies load after hydration, so tags in the static HTML could run first. Add the snippet with onRenderBody and setHeadComponents in gatsby-ssr.js, which puts it in the HTML of every page.
gatsby-ssr.js shapes the HTML Gatsby renders at build time. Run gatsby build and gatsby serve, then view the page source to check that the snippet is the first script in the head.
Listen for the cq:consent event on window, keep whether analytics is in detail.categories, and send the page view from onRouteUpdate in gatsby-browser.js only when it is. The event fires for a stored choice on load and after every new choice.
No. The snippet is one script element added in gatsby-ssr.js. There is no plugin to configure.

Put a quiet banner on your website today

Free for one website with every core feature, no card and no trial clock. Upgrade a website to Starter for $3 a month to match your brand with AI and drop the branding, or to Pro for $9 to design it with the AI agent.