Tilbage til bloggen
Technical

Astro Cookie Consent: The Complete Guide

GetCookies-teametJanuary 3, 20257 min læsning
AstroStatic SiteImplementationTutorial

TLDR: Add cookie consent to Astro in 30 seconds. One script tag in your layout. Works with React, Vue, Svelte, Solid islands. Static sites included.

Read full summary

Astro is the simplest way to build fast websites. Cookie consent should match that simplicity. One script tag gives you GDPR compliance, Google Consent Mode v2, and works with every Astro integration.

Summary by Claude AI

Astro's Simplicity, Extended to Compliance

Astro is refreshingly simple. Write HTML. Add some scripts. Ship fast sites.

Cookie consent tools often fight this simplicity. They want npm packages, build plugins, and configuration files. They add complexity where there should be none.

We follow Astro's philosophy: add a script tag, you're done.

The 30-Second Setup

---
// src/layouts/Layout.astro
---
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width" />
    <script
      src="https://getcookies.co/api/v1/widget/loader.js"
      data-domain-id="YOUR_DOMAIN_ID"
      async
    ></script>
  </head>
  <body>
    <slot />
  </body>
</html>

Every page using this layout now has:

  • GDPR-compliant cookie banner
  • Google Consent Mode v2 (all four signals)
  • Script blocking until consent
  • Third-party pixel support

No npm install. No configuration file. No build step.

Environment Variables

Use Astro's environment variables for different domains:

---
// src/layouts/Layout.astro
const domainId = import.meta.env.PUBLIC_GETCOOKIES_DOMAIN_ID
---
<html>
  <head>
    {domainId && (
      <script
        src="https://getcookies.co/api/v1/widget/loader.js"
        data-domain-id={domainId}
        async
      ></script>
    )}
  </head>
  <body>
    <slot />
  </body>
</html>
# .env
PUBLIC_GETCOOKIES_DOMAIN_ID=your-domain-id

Island Architecture: Perfect Compatibility

Astro's island architecture works seamlessly with GetCookies. The consent script runs globally. Your interactive islands check consent when they need to.

React Island

// src/components/Analytics.tsx
import { useEffect, useState } from 'react'

export default function Analytics() {
  const [hasConsent, setHasConsent] = useState(false)

  useEffect(() => {
    const checkConsent = () => {
      setHasConsent(window.GetCookies?.hasConsent('analytics') ?? false)
    }

    checkConsent()
    window.addEventListener('getcookies:consent', checkConsent)
    return () => window.removeEventListener('getcookies:consent', checkConsent)
  }, [])

  useEffect(() => {
    if (hasConsent) {
      // Initialize analytics
      console.log('Analytics enabled')
    }
  }, [hasConsent])

  return null
}
---
import Layout from '../layouts/Layout.astro'
import Analytics from '../components/Analytics'
---
<Layout>
  <Analytics client:load />
  <h1>Welcome</h1>
</Layout>

Vue Island

<!-- src/components/CookieSettings.vue -->
<script setup>
import { ref, onMounted, onUnmounted } from 'vue'

const hasAnalytics = ref(false)
const hasMarketing = ref(false)

const checkConsent = () => {
  hasAnalytics.value = window.GetCookies?.hasConsent('analytics') ?? false
  hasMarketing.value = window.GetCookies?.hasConsent('marketing') ?? false
}

const showPreferences = () => {
  window.GetCookies?.showPreferences()
}

onMounted(() => {
  checkConsent()
  window.addEventListener('getcookies:consent', checkConsent)
})

onUnmounted(() => {
  window.removeEventListener('getcookies:consent', checkConsent)
})
</script>

<template>
  <div>
    <p>Analytics: {{ hasAnalytics ? 'Enabled' : 'Disabled' }}</p>
    <p>Marketing: {{ hasMarketing ? 'Enabled' : 'Disabled' }}</p>
    <button @click="showPreferences">Change Settings</button>
  </div>
</template>

Svelte Island

<!-- src/components/ConsentStatus.svelte -->
<script>
  import { onMount } from 'svelte'

  let hasAnalytics = false
  let hasMarketing = false

  function checkConsent() {
    hasAnalytics = window.GetCookies?.hasConsent('analytics') ?? false
    hasMarketing = window.GetCookies?.hasConsent('marketing') ?? false
  }

  function showPreferences() {
    window.GetCookies?.showPreferences()
  }

  onMount(() => {
    checkConsent()
    window.addEventListener('getcookies:consent', checkConsent)
    return () => window.removeEventListener('getcookies:consent', checkConsent)
  })
</script>

<div>
  <p>Analytics: {hasAnalytics ? 'On' : 'Off'}</p>
  <button on:click={showPreferences}>Cookie Settings</button>
</div>

Conditional Script Loading

Method 1: Data Attributes

Let GetCookies manage script activation:

---
// src/layouts/Layout.astro
---
<html>
  <head>
    <!-- GetCookies first -->
    <script
      src="https://getcookies.co/api/v1/widget/loader.js"
      data-domain-id="YOUR_DOMAIN_ID"
      async
    ></script>

    <!-- Analytics only after consent -->
    <script
      type="text/plain"
      data-cookieconsent="analytics"
      src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXX"
    ></script>

    <!-- Marketing pixels only after consent -->
    <script type="text/plain" data-cookieconsent="marketing">
      // Meta Pixel code here
    </script>
  </head>
  <body>
    <slot />
  </body>
</html>

The type="text/plain" prevents execution. GetCookies activates scripts after consent.

Method 2: Inline Script

Load scripts dynamically on consent:

<script>
  window.addEventListener('getcookies:consent', (e) => {
    const { analytics, marketing } = e.detail

    if (analytics) {
      const script = document.createElement('script')
      script.src = 'https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXX'
      document.head.appendChild(script)
    }
  })
</script>

Static Sites: Perfect Fit

Astro often generates static sites. GetCookies is ideal for this:

  1. No server required - GetCookies runs entirely client-side
  2. CDN-friendly - Works on any static host
  3. No build-time data - Consent state is runtime-only

Hosts That Work

  • Netlify
  • Vercel
  • Cloudflare Pages
  • GitHub Pages
  • AWS S3 + CloudFront
  • Any static file server

Common Patterns

Footer Cookie Link

---
// src/components/Footer.astro
---
<footer>
  <nav>
    <a href="/privacy">Privacy Policy</a>
    <button id="cookie-settings">Cookie Settings</button>
  </nav>
</footer>

<script>
  document.getElementById('cookie-settings')?.addEventListener('click', () => {
    window.GetCookies?.showPreferences()
  })
</script>

Consent-Aware Component

---
// src/components/PersonalizedSection.astro
---
<div id="personalized-section">
  <div id="generic-content">
    <p>Sign in for personalized recommendations</p>
  </div>
  <div id="personalized-content" style="display: none;">
    <!-- Personalized content here -->
  </div>
</div>

<script>
  function updateContent() {
    const hasConsent = window.GetCookies?.hasConsent('marketing') ?? false
    const generic = document.getElementById('generic-content')
    const personalized = document.getElementById('personalized-content')

    if (generic && personalized) {
      generic.style.display = hasConsent ? 'none' : 'block'
      personalized.style.display = hasConsent ? 'block' : 'none'
    }
  }

  if (document.readyState === 'complete') {
    updateContent()
  } else {
    window.addEventListener('load', updateContent)
  }

  window.addEventListener('getcookies:consent', updateContent)
</script>

TypeScript Support

Add types to your env.d.ts:

// src/env.d.ts
/// <reference types="astro/client" />

interface Window {
  GetCookies?: {
    hasConsent: (category: 'analytics' | 'marketing' | 'preferences') => boolean
    showPreferences: () => void
    acceptAll: () => void
    rejectAll: () => void
  }
}

View Transitions

With Astro's View Transitions, GetCookies persists across navigations:

---
// src/layouts/Layout.astro
import { ViewTransitions } from 'astro:transitions'
---
<html>
  <head>
    <ViewTransitions />
    <script
      src="https://getcookies.co/api/v1/widget/loader.js"
      data-domain-id="YOUR_DOMAIN_ID"
      async
    ></script>
  </head>
  <body>
    <slot />
  </body>
</html>

Users won't see the banner again after choosing. State persists across page transitions.

Content Collections: Consent-Aware Blog

For blogs with consent-dependent features:

---
// src/pages/blog/[slug].astro
import { getCollection } from 'astro:content'
import Layout from '../../layouts/Layout.astro'

export async function getStaticPaths() {
  const posts = await getCollection('blog')
  return posts.map(post => ({
    params: { slug: post.slug },
    props: { post }
  }))
}

const { post } = Astro.props
const { Content } = await post.render()
---
<Layout>
  <article>
    <Content />
  </article>

  <!-- Comments only with consent -->
  <div id="comments-section">
    <p id="comments-blocked">Enable cookies to view comments</p>
    <div id="comments" style="display: none;"></div>
  </div>
</Layout>

<script>
  function updateComments() {
    const hasConsent = window.GetCookies?.hasConsent('preferences') ?? false
    const blocked = document.getElementById('comments-blocked')
    const comments = document.getElementById('comments')

    if (blocked && comments) {
      blocked.style.display = hasConsent ? 'none' : 'block'
      comments.style.display = hasConsent ? 'block' : 'none'

      if (hasConsent) {
        // Load comments widget
      }
    }
  }

  window.addEventListener('getcookies:consent', updateComments)
  window.addEventListener('load', updateComments)
</script>

Performance Impact

Metric Impact
Bundle size +~1.6 KB loader (gzipped); banner ~20 KB, loaded async
LCP No impact
FID No impact
CLS No impact

Your Lighthouse 100s stay at 100.

Deployment

Netlify

npm run build
netlify deploy --prod

Vercel

vercel --prod

Cloudflare Pages

npm run build
npx wrangler pages deploy dist

What You Don't Have to Do

  • Install any npm package
  • Create any configuration file
  • Set up any build plugin
  • Configure GTM triggers
  • Read Consent Mode documentation
  • Debug SSR issues (there's no SSR to debug)

One script tag. Full compliance. The Astro way.


Ready to add cookie consent to your Astro site?

Get your domain ID in 60 seconds

G

GetCookies-teamet

Skrevet og vedligeholdt af GetCookies-teamet hos Getia AS i Oslo, Norge. Artiklen er generel information om cookiesamtykke og ikke juridisk rådgivning.

Scan dit eget website gratis

Gratisplanen kører banneret på ét website med tre cookie-scanninger om måneden. Intet kort, intet salgsopkald.