Cookie Consent for SvelteKit needs to work across the first page load and the route changes that follow it. This guide covers a practical setup for SvelteKit, CookieScript, Google Analytics 4 (GA4) and client-side navigation, including Google Consent Mode and Google Tag Manager (GTM), without losing page views, counting them twice or running analytics before the intended consent state allows it.
Why SvelteKit Changes the Cookie-Consent Setup
A SvelteKit site does not necessarily reload the whole document each time a visitor follows an internal link.
On the first request, a page may be rendered on the server. SvelteKit then hydrates the page in the browser. After that, its router can handle internal navigation on the client.
That matters for both Cookie Consent and analytics.
Code used during server-side rendering (SSR) cannot assume that window, document, browser storage or an analytics global already exists. At the same time, GA4 cannot rely only on traditional page loads if visitors move between routes without requesting a new HTML document.
This does not make every SvelteKit site a single-page application (SPA). SvelteKit supports SSR, prerendering and client-side rendering (CSR), and a true SPA is a separate configuration choice.
For a SvelteKit cookie consent implementation, keep two things separate from the start: the visitor's consent state and the current route. CookieScript events should represent consent activity. SvelteKit navigation APIs should represent navigation.
Decide How CookieScript and GA4 Will Be Installed
There are two separate decisions to make before adding route listeners or analytics events.
First, decide how CookieScript will be installed. A direct installation can live at application level in src/app.html. It can also be installed through its GTM template where that method fits the banner configuration. Geo-targeted installations are an exception, covered below.
Second, decide how GA4 page views will be measured:
Automatic GA4 history measurement. Start here with a direct Google tag when GA4's automatic browser-history measurement correctly records SvelteKit client-side navigation.
Deliberately manual SvelteKit page views. Use this when automatic measurement does not fit the application or you need explicit control over page views.
GTM history-based page views. In this version, GTM owns client-side route measurement and the relevant Google tags.
These page-view approaches are alternatives. Combining them for the same navigation is a common way to create duplicate analytics.
For example, if GA4 Enhanced Measurement already records a history change, a second page_view from GTM and a third from afterNavigate do not make the measurement more reliable. They count the same page view or navigation more than once.
Choose one mechanism to own page-view measurement.
Add CookieScript to SvelteKit
For a direct installation, CookieScript's banner installation guidance places the generated code in the website header, before scripts that it needs to control, on every page where it should appear and using the required geo-targeting option where applicable.
In a standard SvelteKit project, src/app.html is the HTML page template. Its <head> is therefore the appropriate application-level location for adapting the direct installation.
This adapts CookieScript's general installation model to SvelteKit's application structure.
<!-- src/app.html -->
<head>
<!-- Use the banner code generated in your CookieScript account -->
<script
type="text/javascript"
charset="UTF-8"
src="https://cdn.Cookie-Script.com/s/COOKIE-SCRIPT-BANNER-ID.js">
</script>
%sveltekit.head%
</head>
Use the actual code generated for your banner. Do not construct a production banner URL from the placeholder above.
This is particularly important if you use geo-targeting, because the installation requirements depend on the configured setup.
There is no reason to inject the same banner again from onMount, afterNavigate or individual +page.svelte files. Once it is installed at application level, a route transition is not a new installation.
That distinction also keeps the setup easier to debug. If the banner suddenly appears twice, there are fewer possible loading points to inspect.
If the banner is installed through GTM instead, use that as the intended installation method rather than also loading the same banner directly. CookieScript's GTM installation guide uses the CookieScript Community Template with the Consent Initialization – All Pages trigger.
There is an important exception for geo-targeting. CookieScript's current guidance requires geo-targeted banner code to be installed directly in the website <head>, not through GTM. Global or non-geo-targeted code can be installed through GTM.
Do not independently load the same CookieScript banner from src/app.html and GTM.
Keep Browser-Only Code Out of Server Execution
Some SvelteKit code can run on the server before it ever runs in a browser.
This becomes important as soon as analytics code reads window, document, local storage or a global such as gtag.
SvelteKit provides browser through $app/environment when you need to distinguish the browser environment:
<script>
import { browser } from '$app/environment';
if (browser) {
// Browser-only integration code
}
</script>
Svelte's onMount is another option when work should happen after a component has mounted in the browser.
Use these tools where they fit the job. Do not disable SSR across an application merely to make analytics code easier to write.
There is also little benefit in wrapping a global banner installation in component lifecycle code when it can live in src/app.html.
Configure GA4 Around the Visitor's Consent Choice
Google Consent Mode and route measurement solve different problems.
Consent Mode tells supported Google tags how to behave for the consent state available at that moment. Relevant consent types include:
analytics_storagead_storagead_user_dataad_personalization
Google distinguishes Basic and Advanced Consent Mode.
With Basic Consent Mode, Google tags are blocked until the required consent has been granted. With Advanced Consent Mode, Google tags can load with denied consent defaults and change their behaviour after an update to the visitor's choice.
CookieScript provides Google Consent Mode v2 support. When GTM is used, the current implementation guidance recommends the CookieScript GTM template; a separate non-GTM implementation path is also documented.
Consent Mode deals with the permission state supplied to Google tags. GA4 still needs a mechanism that decides when a page view has happened.
The same separation matters for script blocking.
CookieScript supports automatic and manual script blocking, while GTM and cookie consent can also be configured around consent-aware tag behaviour. Custom Svelte code could theoretically create another loading condition.
Avoid putting all three in charge of the same script.
If a GA4 tag fails to run, you should be able to identify which layer prevented it. Multiple blocking systems covering the same tag make that much harder.
Google Consent Mode also does not become a consent-control system for unrelated technologies. Meta Pixel and other non-Google scripts need their own appropriate CookieScript, GTM or blocking configuration.
Make Sure GA4 Measures SvelteKit Client-Side Navigation
Before writing any SvelteKit route-tracking code, find out what GA4 already records.
This is one of the most important checks in a SvelteKit Google Analytics setup.
GA4 Enhanced Measurement can record page views when the browser history state changes. SvelteKit uses browser-side navigation after hydration in the normal client-routing flow, so automatic history measurement may already cover the routes you need.
Test it.
Start with a direct visit to an internal route. Then follow links through several pages. Use the Back and Forward buttons. Test query-string changes if those represent meaningful views on your site.
For every intended view, check whether GA4 receives one page_view.
Also inspect page_location, the page title and, where relevant, referrer information.
If automatic history measurement works, stop there. A SvelteKit GA4 implementation does not need an afterNavigate callback merely because the framework supports one.
When manual page views make sense
Manual measurement is useful when the automatic behaviour does not correctly represent the application or when the analytics design deliberately requires manual control.
First remove the competing automatic page views.
For a direct Google tag, that can include disabling the normal initial page view:
gtag('config', 'G-XXXXXXXXXX', {
send_page_view: false
});
That setting alone is not enough if GA4 Enhanced Measurement is still configured to send page views from browser-history changes. Turn off that competing history measurement as well when moving to a fully manual architecture.
SvelteKit's afterNavigate hook can then be used in a root layout:
<!-- src/routes/+layout.svelte -->
<script>
import { afterNavigate } from '$app/navigation';
let { children } = $props();
afterNavigate(() => {
if (typeof window.gtag !== 'function') return;
window.gtag('event', 'page_view', {
page_title: document.title,
page_location: window.location.href
});
});
</script>
{@render children()}
There is an important detail here.
afterNavigate does not only run after the visitor moves from one route to another. It also runs when the component first mounts.
That first call can be useful in a fully manual setup because it can provide the initial page view. It can also produce a duplicate if another mechanism has already recorded that page.
In a manual setup, check the initial document load, the first afterNavigate invocation and subsequent route changes separately.
There is another timing issue with Basic Consent Mode. If the Google tag is unavailable until the visitor grants consent, an afterNavigate callback that ran before the tag existed cannot send an event through window.gtag.
In that architecture, coordinate the first permitted manual page view with the point at which the Google tag becomes available after consent, so the first eligible view is not missed.
Account for that in the consent and analytics design rather than assuming the first callback will automatically be replayed.
The minimal example above sends page_title and page_location. It should not be treated as proof that virtual-route referrer information is also correct. If referrer attribution matters to the implementation, verify page_referrer separately.
Using Google Tag Manager Instead
GTM can own the client-side route-measurement architecture instead of SvelteKit code.
Google's current approach for applications that update views through browser history uses a History Change trigger for subsequent virtual page views. In this architecture, automatic GA4 history-based page views should be disabled so GTM does not duplicate them, while the intended initial-page measurement remains part of the setup.
The GTM setup can update values such as page_location and page_title for the new view before sending the event.
For a SvelteKit GTM deployment, keep client-side route measurement in GTM. A SvelteKit afterNavigate listener is not needed to report the same route a second time.
The banner can also be installed through the GTM Community Template, provided it is not using the GEO-targeting installation that must be placed directly in the website <head>. Use the appropriate Consent Initialization trigger so the consent state is established at the correct stage of the GTM lifecycle.
Do not let direct gtag.js, GTM and custom SvelteKit code send duplicate GA4 page views for the same navigation.
Handle Consent Changes Separately From Route Changes
CookieScript provides custom events for consent interactions, consent categories and initialisation.
CookieScriptAcceptAll, CookieScriptAccept and CookieScriptReject can be used to respond to consent interactions.
CookieScriptLoaded is different. It fires when CookieScript.Instance is ready, so it is an initialisation event rather than a new consent-choice event.
Category-specific events are also available. They can fire once per page and can fire on page load when the visitor has already accepted the relevant category.
Use consent events for consent behaviour: a new choice, changed preferences, category-specific logic or application initialisation where appropriate.
Do not use them to detect SvelteKit navigation.
A visitor might keep the same stored consent preference for weeks while opening dozens of routes. There is no reason for that stored choice to become a new consent action on each route.
The reverse is also true. A visitor can reopen the banner and change a preference while staying on the same page.
Test Consent and Analytics Across SvelteKit Navigation
Do not finish testing when the cookie banner appears.
Start with a browser that has no saved consent state.
- First visit: confirm that the expected banner appears and check the default consent state.
- Before making a choice: inspect which scripts, storage operations and network requests are allowed.
- Reject non-essential cookies: check the resulting cookies, Google tags and other trackers.
- Accept analytics: confirm that GA4 becomes available according to the selected architecture.
- Accept all: check the consent-state update and the technologies that are now permitted.
- Change preferences later: test granting and withdrawing categories.
- Reload the page: make sure the saved preference is applied correctly.
- Navigate across several SvelteKit routes: check the expected GA4 page views.
- Use the browser Back button: inspect the resulting history navigation.
- Use the Forward button: repeat the check.
- Test relevant query-string changes: decide whether they should represent separate views and compare that with the actual measurement.
- Enter an internal URL directly: test the initial SSR and hydration path rather than only client-side links.
- Test the production build: local development behaviour is not a substitute for checking the deployed application.
- Repeat important flows on mobile and major browsers.
Browser DevTools are useful throughout this process.
Check cookies and local storage. Look at which scripts have loaded. Inspect console errors and network requests. For GA4, examine the relevant collect requests where applicable.
With Advanced Consent Mode, the presence of a Google request does not by itself tell you that analytics storage was granted. Interpret what you see in the context of the configured consent state.
Google Tag Assistant can help inspect consent defaults, updates and tag activity. GA4 DebugView is useful for checking the events that reach the property.
Two failures deserve particular attention.
Missing page views: the initial page appears in GA4, but routes opened through SvelteKit client-side navigation do not. Also check for cases where analytics does not become available as intended after the visitor grants the relevant consent.
Duplicate page views: two measurement systems respond to the same navigation. Enhanced Measurement plus GTM History Change is one example. An automatic initial page view followed by an afterNavigate initial page view is another. Direct gtag.js and GTM can also duplicate events if both are configured to send them.
Common SvelteKit Cookie-Consent and GA4 Mistakes
- SSR browser-global access: accessing
window,documentor analytics globals during SSR. - Duplicate CookieScript loading: loading the banner from more than one place or injecting it again after route changes.
- Consent-model conflicts: loading GA4 in a way that conflicts with the intended consent model.
- Multiple blocking authorities: using several independent systems to block the same tracker.
- Automatic + manual tracking: combining automatic GA4 history measurement with manual route page views.
- Duplicate initial page: counting the initial page twice because another mechanism is active when
afterNavigatefirst runs. - Missing client-side routes: recording the first page while missing later client-side routes.
- Duplicate listeners: adding the same route listener more than once.
- Unclear page-view ownership: mixing direct
gtag.js, GTM and SvelteKit page-view code without deciding which mechanism owns each event. - Consent events used as route events: treating a CookieScript consent event as a SvelteKit route event.
- Development-only testing: relying on local development tests without checking production.
- Assuming every site is an SPA: describing a standard SvelteKit deployment as a pure SPA without checking its configuration.
Managing Consent With CookieScript
CookieScript handles the consent side of this architecture. SvelteKit handles application rendering and routing, while GA4 or GTM handles the selected page-view strategy.
CookieScript is a Consent Management Platform (CMP) that Google includes among the CMP partners available for Consent Mode setup. It is also a Google-certified CMP with Gold tier status.
Depending on your plan and setup, relevant tools include:
- Cookie Banner for presenting consent choices and configuring the banner shown to website visitors.
- Global privacy regulation support helps websites meet cookie-consent and privacy requirements under major frameworks, including the GDPR and ePrivacy Directive in Europe, CCPA and CPRA in California, LGPD in Brazil, and PIPEDA in Canada.
- Google Consent Mode v2 for providing supported consent types to Google tags.
- Google Tag Manager integration through the Community Template for setups where that installation method is appropriate.
- Cookie Scanner for identifying cookies and related technologies used by the site.
- User consents recording for collecting and retaining records of visitors' cookie choices.
- Third-party cookie blocking for controlling third-party technologies according to the selected consent setup.
- Geo targeting for applying different banner behaviour based on visitor location.
- Consent events for application or GTM logic that needs to respond to visitor choices and relevant consent categories.
Additional tools for multilingual websites, automation, reporting and consent management include:
- 42 languages for multilingual Cookie Banners and cookie information.
- Automatic monthly scans to rescan websites and update the cookie declaration as detected cookies change.
- Automatic script blocking for controlling applicable third-party scripts before the relevant consent is available.
- Advanced reporting for analysing Cookie Banner interactions, acceptance, rejection and category preferences.
- Cookie Banner sharing for sharing banner access between accounts where supported by the selected plan.
- IAB TCF 2.3 integration for setups that use the IAB Europe Transparency & Consent Framework.
- Privacy Policy Generator for creating a Privacy Policy for a website or business.
- Cookie Policy Generator for creating the cookie-policy information that accompanies the Privacy Policy Generator.
A 14-day free trial of the Plus plan is also available without requiring a credit card.
Conclusion
A reliable SvelteKit cookie-consent setup starts by assigning clear responsibilities. Install CookieScript using the method appropriate to the banner configuration, keep consent state separate from navigation state, and decide which single mechanism owns GA4 page views.
Then test the initial load, consent changes and client-side navigation separately. If every intended route produces one page view under the intended consent state, the different parts of the implementation are doing their jobs.
Frequently Asked Questions
Where should CMP be added in SvelteKit?
For a direct installation, add the generated CookieScript code to the <head> in src/app.html, before scripts it needs to control, following the current installation guidance. Geo-targeted code should also be installed directly in the website <head>. If a global or non-geo-targeted banner is deployed through the GTM template instead, use that as the intended installation method rather than loading the same banner directly as well.
Does CookieScript reload after every SvelteKit route change?
No route-by-route banner injection is needed. A direct installation belongs at application level, while an appropriate GTM installation is handled through the selected GTM setup. Moving to another SvelteKit route does not itself represent a new consent choice.
Why does GA4 record only the first page in SvelteKit?
The first document load may be measured while later browser-side navigation is missed. Check whether GA4 Enhanced Measurement detects the History API changes used by the application. If you use GTM or manual measurement instead, confirm that the relevant history trigger or route callback runs for subsequent navigation.
Why does GA4 record two page views per route?
Usually because more than one measurement mechanism is active. Check for GA4 automatic history measurement, GTM History Change triggers, direct gtag.js events and SvelteKit afterNavigate callbacks. The same navigation should have one intended page-view owner.
Should I use afterNavigate for GA4?
Only when you have chosen a manual page-view architecture. Check GA4 automatic history measurement first. If manual tracking is necessary, disable competing automatic page views and remember that afterNavigate also runs when its component first mounts, so the initial page needs to be handled deliberately. Also verify values such as page_referrer rather than assuming a minimal manual callback reproduces all of GA4's desired virtual-navigation context automatically.
Can CookieScript and GTM be used with SvelteKit?
Yes. GTM can manage Google tags and client-side route measurement in a SvelteKit application, and the CookieScript Community Template can be used where that installation method fits the banner configuration. Keep one page-view architecture and avoid sending the same GA4 event from both GTM and SvelteKit code.
Does Google Consent Mode track SvelteKit route changes?
No. Google Consent Mode communicates consent state to supported Google tags. It does not determine when a SvelteKit route should count as a page view. That job belongs to GA4 automatic history measurement, GTM history triggers or a deliberately manual navigation implementation.
How do I test CookieScript and GA4 after client-side navigation?
Test with no saved preference, after rejection, after analytics consent, after full consent and after changing the preference later. Then test direct page entry, several internal routes, Back and Forward navigation and relevant query-string changes. Use browser DevTools, Google Tag Assistant and GA4 DebugView to check both consent behaviour and page-view measurement.

