# How to add a cookie consent banner to a Vue 3 app

> In a Vue 3 app built with Vite, put the snippet in index.html at the project root, in the head and above the script that loads src/main. Mark tracking tags in the same file, and read the visitor's choice in components with a small composable that listens to Cookiequiet's cq:consent event; send route-change page views from router.afterEach only when analytics is granted.

Source: https://cookiequiet.com/platforms/vue
Published: 2026-10-21
Publisher: Cookiequiet

## Steps

1. **Copy the snippet** In the Cookiequiet dashboard, add the website and copy the snippet with your site key, data-consent-mode="1" and data-auto-block="1".
2. **Paste it in index.html** Open index.html at the root of the Vite project and paste the snippet in the head, above the module script that loads src/main.
3. **Mark your tags** Paste analytics and advertising tags below the snippet with type="text/plain" and data-cq-category set to analytics or marketing.
4. **Add a useConsent composable** Create a composable that keeps the granted categories in a ref, starting from window.Cookiequiet.get() and updated on the cq:consent event. Use it with v-if around embeds.
5. **Gate route-change page views** In the router setup, send page views from router.afterEach only when analytics is granted.
6. **Add the Cookie preferences link** In the footer component, add a plain anchor with href="#cq-preferences" and the data-cq-show-preferences attribute.
7. **Build and test** Run vite build and vite preview, open the app 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 a Vue app?

A Vue 3 app created with `create-vue` uses Vite, whose entry point is `index.html` at the project root. Paste the snippet in its head, above the module script that loads your app:

```html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <script src="https://cookiequiet.com/cq.js" data-site="%VITE_COOKIEQUIET_SITE%" data-consent-mode="1" data-auto-block="1" defer></script>
    <title>My app</title>
  </head>
  <body>
    <div id="app"></div>
    <script type="module" src="/src/main.ts"></script>
  </body>
</html>
```

Vite replaces `%VITE_COOKIEQUIET_SITE%` with the variable from your `.env` file and leaves the text unchanged when the variable is not defined, so check the built file or write the key directly. Module scripts run after parsing, in document order with deferred scripts, so `cq.js` above the entry script runs before your app.

The snippet stays in HTML: do not add it from a component or bundle it. It reads its settings from its own tag and must run before the tags it controls. If you use Nuxt rather than plain Vue, follow the [Nuxt guide](/platforms/nuxt).

## How do I block my tags until consent?

Paste tracking tags under the snippet in `index.html` and change their opening tag:

```html
<script type="text/plain" data-cq-category="analytics" src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXX"></script>
<script type="text/plain" data-cq-category="analytics">
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  gtag('js', new Date());
  gtag('config', 'G-XXXXXXX');
</script>
```

Cookiequiet runs them when the category is granted, and at once on later visits where the stored choice includes it. To keep the Google tag loading under Consent Mode instead, see the [Google Analytics 4 guide](/platforms/google-analytics-4). Scripts and iframes that your code or a library creates at runtime are held by the auto-blocker when they come from one of the about 80 trackers it knows.

## How do components read the choice?

Cookiequiet dispatches a `cq:consent` event on `window` each time it applies a choice: a stored one on load, a new one after a click, and every category in regions where geo-targeting does not show the banner. `event.detail.categories` lists the granted categories; `window.Cookiequiet.get()` returns the choice in force (categories in `c`, `a: "geo"` where no banner is shown) or `null` while no choice applies yet. A composable:

```ts
// src/composables/useConsent.ts
import { ref } from "vue";

const cq = (window as any).Cookiequiet;
const granted = ref<string[]>(cq?.get()?.c ?? []);

window.addEventListener("cq:consent", (e) => {
  granted.value = (e as CustomEvent).detail.categories;
});

export function useConsent() {
  return granted;
}
```

In a component:

```vue
<script setup lang="ts">
import { useConsent } from "@/composables/useConsent";
const granted = useConsent();
const openPreferences = () => (window as any).Cookiequiet.showPreferences();
</script>

<template>
  <iframe v-if="granted.includes('marketing')" src="https://www.youtube-nocookie.com/embed/VIDEO_ID" title="Video"></iframe>
  <button v-else @click="openPreferences">Allow videos</button>
</template>
```

## What about route changes?

Vue Router changes the URL without reloading. The snippet runs once per full load, the choice stays applied and the banner does not reappear. Marked scripts are activated on load and after each choice, so keep them in `index.html` rather than in components. For page views per route, use a global after hook, which Vue Router calls after each navigation:

```ts
// src/router/index.ts
import { useConsent } from "@/composables/useConsent";
const granted = useConsent();

router.afterEach((to) => {
  if (granted.value.includes("analytics")) sendPageView(to.fullPath); // your analytics call
});
```

## How do I add the Cookie preferences link?

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

Cookiequiet handles clicks on this link anywhere in the page and opens the preferences centre.

## How do I test it?

1. Run `vite build` and `vite preview`, then view the page source: `cq.js` is the first script.
2. Open the app in a private window and click **Reject all**: the cookies are `cq_consent` and your own.
3. Clear the cookies, click **Accept all**, reload: the marked tags run. Change routes and check the page views.
4. Open the consent log in the dashboard.

A Content Security Policy must allow `https://cookiequiet.com` in `script-src` and `connect-src`. Cookiequiet is free for one website with geo-targeting; Starter is €3 per website per month, with no pageview limit on any plan.

## Questions

### Is there a Vue plugin for Cookiequiet?

No, and none is needed. The banner is one script tag in index.html. Components read the choice through the cq:consent event and window.Cookiequiet.get(), which a composable of a few lines wraps.

### Does the banner reappear when Vue Router changes the route?

No. The page is not reloaded, so the script runs once and the stored choice stays applied across routes.

### Does it work with Vue 2?

The snippet does: it only needs the HTML file that loads the app, which is public/index.html in a Vue CLI project. The composable above uses the Vue 3 ref API; in Vue 2 keep the same event listener in a mixin or a small store.

### How do I track page views only after consent?

Register router.afterEach and send the page view only when the granted categories include analytics. The categories come from the cq:consent event Cookiequiet dispatches on load and after every choice.

## Sources

- [Vue.js: Quick Start (create-vue and Vite)](https://vuejs.org/guide/quick-start)
- [Vite: Env variables and modes (HTML constant replacement)](https://vite.dev/guide/env-and-mode)
- [Vue Router: Navigation guards (global after hooks)](https://router.vuejs.org/guide/advanced/navigation-guards)
- [Vue.js: Composables](https://vuejs.org/guide/reusability/composables)
