Skip to main content
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>:
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):

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:

SvelteKit

Add the script to src/app.html inside <head> for the initial load, then re-inject on subsequent navigations from your root layout:

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:

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:
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:
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).
The cc:onUpdate event fires when the user changes their consent from the preferences modal.
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.