Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.cookiechimp.com/docs/llms.txt

Use this file to discover all available pages before exploring further.

Single Page Applications (SPAs) update the URL and the visible page without a full page reload. Because of this, a script tag added to <head> only runs once — on the first load — and the CookieChimp widget can be torn down or lost when the user navigates between routes. This page covers two things:
  1. How to load the CookieChimp script so it runs on every page, framework by framework.
  2. How to position the Privacy Trigger so the floating icon survives client-side navigation.
We have dedicated guides for the most common frameworks — they’re more detailed than this overview: Next.js · React (Vite/CRA) · Remix · Nuxt · Angular · Astro · SvelteKit. This page is the catch-all for any SPA that doesn’t have its own guide.

How do I install CookieChimp in an SPA?

The pattern is the same across all SPA frameworks:
  1. Inject the script into <head> so it loads as early as possible — ideally before the framework hydrates.
  2. Re-initialize the widget on every client-side route change by listening to the framework’s navigation event.
The exact navigation event differs per framework. Pick yours below. The reinit pattern is the same across frameworks: remove the existing CookieChimp script tag (if any) and append a fresh one. Loading the script again triggers CookieChimp to rescan the newly mounted DOM. There’s no public reinitialize() API — script re-injection is the documented mechanism.

Astro

Astro’s View Transitions emit an astro:page-load event after every navigation including the first, so this single handler covers both initial load and subsequent transitions — no separate static <script src> needed. Use is:inline so Astro doesn’t bundle the script. Add this to your root layout — typically src/layouts/Layout.astro — inside <head>:
<script is:inline>
  function runCookieChimp() {
    document.getElementById("cookiechimp-js")?.remove();

    var script = document.createElement("script");
    script.src = "https://cookiechimp.com/widget/YOUR_ACCOUNT_ID.js";
    script.id = "cookiechimp-js";
    document.head.appendChild(script);
  }

  // Fires on first load AND after every Astro view transition.
  document.addEventListener("astro:page-load", runCookieChimp);
</script>
Replace YOUR_ACCOUNT_ID with your actual CookieChimp Account ID from your dashboard.
Not using View Transitions? If you haven’t enabled <ViewTransitions /> in your layout, Astro performs a full page reload on each navigation — the standard <script src="..."> install works without any re-initialization logic.

Vue (Vue Router)

Add the script tag in your index.html <head> for the initial load, then re-inject on every subsequent route change from your router config (src/router/index.js):
import { createRouter, createWebHistory } from "vue-router";

const router = createRouter({
  history: createWebHistory(),
  routes: [/* ... */],
});

let isInitial = true;
router.afterEach(() => {
  if (isInitial) {
    isInitial = false;
    return;
  }

  document.getElementById("cookiechimp-js")?.remove();

  const script = document.createElement("script");
  script.src = "https://cookiechimp.com/widget/YOUR_ACCOUNT_ID.js";
  script.id = "cookiechimp-js";
  document.head.appendChild(script);
});

export default router;

React (React Router v6+)

Add the script tag in your index.html <head> for the initial load, then mount a tiny component that re-injects on subsequent route changes:
// src/components/CookieChimpReinit.tsx
import { useEffect, useRef } from "react";
import { useLocation } from "react-router-dom";

export default function CookieChimpReinit() {
  const { pathname } = useLocation();
  const isFirstRender = useRef(true);

  useEffect(() => {
    if (isFirstRender.current) {
      isFirstRender.current = false;
      return;
    }

    document.getElementById("cookiechimp-js")?.remove();

    const script = document.createElement("script");
    script.src = "https://cookiechimp.com/widget/YOUR_ACCOUNT_ID.js";
    script.id = "cookiechimp-js";
    document.head.appendChild(script);
  }, [pathname]);

  return null;
}
// src/main.tsx
<BrowserRouter>
  <CookieChimpReinit />
  <App />
</BrowserRouter>

SvelteKit

Add the script to src/app.html inside <head> for the initial load, then re-inject on subsequent navigations from your root layout:
<!-- src/routes/+layout.svelte -->
<script>
  import { afterNavigate } from "$app/navigation";
  import { browser } from "$app/environment";

  let isInitial = true;

  afterNavigate(() => {
    if (!browser) return;
    if (isInitial) {
      isInitial = false;
      return;
    }

    document.getElementById("cookiechimp-js")?.remove();

    const script = document.createElement("script");
    script.src = "https://cookiechimp.com/widget/YOUR_ACCOUNT_ID.js";
    script.id = "cookiechimp-js";
    document.head.appendChild(script);
  });
</script>

<slot />

Generic SPA (no router events)

If your framework doesn’t expose a router event you can hook into, listen for history.pushState and popstate — this fires on both programmatic navigation and back/forward:
<script>
  function runCookieChimp() {
    document.getElementById("cookiechimp-js")?.remove();

    var script = document.createElement("script");
    script.src = "https://cookiechimp.com/widget/YOUR_ACCOUNT_ID.js";
    script.id = "cookiechimp-js";
    document.head.appendChild(script);
  }

  // Patch pushState/replaceState so we get notified on client-side navigation.
  ["pushState", "replaceState"].forEach((method) => {
    var original = history[method];
    history[method] = function () {
      var result = original.apply(this, arguments);
      window.dispatchEvent(new Event("locationchange"));
      return result;
    };
  });
  window.addEventListener("popstate", () => window.dispatchEvent(new Event("locationchange")));
  window.addEventListener("locationchange", runCookieChimp);

  // First load.
  runCookieChimp();
</script>

How do I position the Privacy Trigger in an SPA?

The Privacy Trigger is the floating icon users click to update their consent preferences. By default it’s appended to <body>, which is fine for traditional pages — but in an SPA, you usually want it in a specific spot in your layout and you want it to survive route changes. Place a <div> with the ID cookiechimp-container wherever you want the trigger rendered:
<div id="cookiechimp-container"></div>
Put this in a part of your layout that doesn’t unmount on navigation — typically the root layout, just inside <body>.

Persisting across view transitions

Some frameworks tear down and recreate DOM nodes between routes, which would remove the Privacy Trigger. Use the framework’s “persistent element” attribute to keep it:
<!-- Astro (View Transitions) -->
<div id="cookiechimp-container" transition:persist></div>

<!-- Hotwire Turbo -->
<div id="cookiechimp-container" data-turbo-permanent></div>
See the Astro docs on persisting components and the Turbo docs on persisting elements across page loads.
The cc:onConsented event fires once when the user has made an initial choice (and on subsequent page loads when consent is already stored).
window.addEventListener("cc:onConsented", function (event) {
  if (CookieChimp.acceptedCategory("analytics")) {
    // "analytics" category enabled
  }

  if (CookieChimp.acceptedService("Google Analytics", "analytics")) {
    // "Google Analytics" service enabled
  }
});
The cc:onUpdate event fires when the user changes their consent from the preferences modal.
window.addEventListener("cc:onUpdate", function (event) {
  var detail = event.detail;

  /**
   * detail.cookie
   * detail.changedCategories
   * detail.changedServices
   */

  if (detail.changedCategories.includes("analytics")) {
    if (CookieChimp.acceptedCategory("analytics")) {
      // "analytics" category was just enabled
    } else {
      // "analytics" category was just disabled
    }

    if (detail.changedServices["analytics"].includes("Google Analytics")) {
      if (CookieChimp.acceptedService("Google Analytics", "analytics")) {
        // "Google Analytics" service was just enabled
      } else {
        // "Google Analytics" service was just disabled
      }
    }
  }
});
For all available events, see Callbacks & Events.

Troubleshooting

  • Banner shows on first load but not after navigation — your re-initialization listener isn’t firing. Double-check the framework-specific event name (astro:page-load, router.afterEach, useLocation, afterNavigate).
  • Privacy Trigger disappears after navigation — add the framework’s persistence attribute (transition:persist, data-turbo-permanent) to your #cookiechimp-container div.
  • Banner doesn't appear at all — confirm localhost (or your domain) is in Additional Domains in Account Settings.
  • Enable Debug mode in the CookieChimp dashboard and check the browser console for logs.