How to add a cookie consent banner to a Nuxt app
In Nuxt 3 or 4, declare the snippet in nuxt.config.ts under app.head.script so it is rendered in the head of every page before any other script, or add it with useHead in app.vue. Attributes such as data-site are passed as keys of the script entry, defer: true keeps the defer attribute, and tagPosition: "head" keeps the tag where a consent script belongs.Steps
Where to load the snippet in Nuxt
Nuxt renders the head of every page from app.head in nuxt.config.ts, through Unhead. A script entry is an object whose keys become attributes, so the snippet translates directly:
ts// nuxt.config.tsexport default defineNuxtConfig({app: {head: {script: [{src: "https://cookiequiet.com/cq.js",defer: true,"data-site": "YOUR_SITE_KEY","data-consent-mode": "1","data-auto-block": "1",tagPosition: "head",key: "cookiequiet",},],},},});
Keep it first in the array so it is rendered before any other script of the head. tagPosition accepts head, bodyOpen and bodyClose; head is the default and the right one here. The same object works in a useHead call in app.vue if you prefer to keep it next to other head logic; the key prevents duplicates. On Nuxt 2, the equivalent is head.script in nuxt.config.js.
How to handle the tags your app loads
Cookiequiet activates marked scripts when a stored choice is applied on load and whenever the visitor makes a choice. In a Nuxt app, that means marked tags belong in the server-rendered head, not in components mounted later.
Marked entries. Add them to the same script array after the Cookiequiet entry; inline code goes in innerHTML:
ts{ type: "text/plain", "data-cq-category": "analytics", src: "https://www.googletagmanager.com/gtag/js?id=G-XXXXXXX" },{type: "text/plain","data-cq-category": "analytics",innerHTML: "window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments);} gtag('js', new Date()); gtag('config', 'G-XXXXXXX');",},
The Meta Pixel base code gets "data-cq-category": "marketing". Nothing of these runs before the category is granted.
Consent Mode instead of marking. To keep Google Analytics or Google Ads loading with denied defaults (cookieless pings, modelled data), insert an entry with Google's default consent block in innerHTML between the Cookiequiet entry and the Google tag entry, as shown in the Google Analytics 4 guide. Modules that inject the Google tag for you generally offer a manual initialization or consent option; use it to set the defaults to denied, and Cookiequiet sends the update.
Components that depend on consent. Read the choice in onMounted through the public API: window.Cookiequiet.get() returns the stored choice (its c array lists the granted categories) and window.Cookiequiet.on(callback) calls back after every choice and returns an unsubscribe function. A small composable can expose granted("marketing") to a YouTube component, which renders a placeholder with a "Cookie preferences" button until then. For plain iframes in templates, data-cq-src instead of src is enough.
How to add the Cookie preferences link
In the layout footer, a plain anchor is all it takes; do not route it through NuxtLink:
html<a href="#cq-preferences" data-cq-show-preferences>Cookie preferences</a>
How to test
- Start the app (
nuxi devworks; the banner fetches its configuration from cookiequiet.com) and open it in a private window. - Click Reject all: the cookies are
cq_consentand your own, no_ga, no_fbp. - Clear the cookies, click Accept all, reload: the marked entries run, their cookies appear.
- Navigate between pages: the banner stays closed and the choice is still applied.
- Check the consent log in the dashboard.
What is specific to Nuxt
- SSR, SPA and static. With
ssr: false, Unhead still renders the head on the client; withnuxi generate, the snippet is in every generated page. All three work. - Layers and modules. A layer or module that adds the Google tag adds it to the same head; check the rendered order in the page source and keep the Cookiequiet entry first.
- Content Security Policy. Allow
https://cookiequiet.cominscript-srcandconnect-srcif you set one. - i18n. The banner's language follows the visitor's browser, with every string editable per language; it does not read the Nuxt locale.
If a tag ends up loaded by a plugin you cannot mark, the auto-blocker still holds the scripts and iframes of about 80 known trackers created with JavaScript until consent, and the cookie scanner tells you what is left.