Skip to main content
6 min read

Add Zenovay Analytics to SvelteKit

Zenovay Analytics integrates seamlessly with SvelteKit, giving you real-time visitor insights without adding a single dependency to your project. SvelteKit applications range from fully static sites to server-rendered apps with complex routing, and Zenovay handles all of these modes out of the box. The tracking script loads asynchronously in under 1 KB, automatically detects client-side navigations through the History API, and respects SvelteKit's SSR lifecycle so it never runs on the server where there is no window object. Whether you are shipping a marketing site, a SaaS dashboard, or an e-commerce storefront with SvelteKit, this guide covers everything from basic installation to custom event tracking, user identification, and cookieless mode. You will be up and running in about two minutes.


Quick Setup (2 minutes)

Add one script tag to your SvelteKit root HTML file. No npm packages, no Vite plugins, no adapter changes.

Step 1: Get Your Tracking Code

Sign in to your Zenovay dashboard and copy your tracking code from Settings → Tracking Code.

Step 2: Add to app.html

Open src/app.html, the root HTML template for every SvelteKit page, and add the Zenovay script inside <head>:

src/app.htmlHTML
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <link rel="icon" href="%sveltekit.assets%/favicon.png" />
  <script
    defer
    data-tracking-code="YOUR_TRACKING_CODE"
    src="https://api.zenovay.com/z.js"
  ></script>
  %sveltekit.head%
</head>
<body data-sveltekit-preload-data="hover">
  <div style="display: contents">%sveltekit.body%</div>
</body>
</html>

That's it! Zenovay will automatically track page views across all your SvelteKit routes, including client-side navigations.

Step 3: Verify Installation

Visit your site and check the Zenovay dashboard. You should see a real-time visitor appear within seconds.

Replace YOUR_TRACKING_CODE with your actual tracking code from the Zenovay Dashboard.

SPA Navigation Tracking

SvelteKit uses client-side routing by default. Zenovay automatically detects route changes via the History API (pushState / popstate), so every navigation records a new page view without any extra configuration.

This works with all SvelteKit navigation methods:

  • <a> tag links (SvelteKit intercepts these)
  • goto() programmatic navigation
  • Browser back/forward buttons
  • $app/navigation functions

Custom Events

Track user interactions using the global window.zenovay function. Always guard with a typeof window or browser check to avoid SSR errors.

Using the browser Guard

SvelteKit provides a browser constant that is true only on the client:

src/lib/components/SignupButton.svelteSVELTE
<script lang="ts">
import { browser } from '$app/environment';

export let plan: string;

function handleClick() {
  if (browser && window.zenovay) {
    window.zenovay('track', 'signup_click', { plan });
  }
}
</script>

<button on:click={handleClick}>
Sign Up for {plan}
</button>

Reusable Tracking Helper

Create a utility module for clean, reusable tracking calls:

src/lib/analytics.tsTypeScript
import { browser } from '$app/environment';

export function track(event: string, data?: Record<string, any>) {
if (browser && window.zenovay) {
  window.zenovay('track', event, data);
}
}

export function identify(userId: string, traits?: Record<string, any>) {
if (browser && window.zenovay) {
  window.zenovay('identify', userId, traits);
}
}

export function trackGoal(goalName: string, data?: Record<string, any>) {
if (browser && window.zenovay) {
  window.zenovay('goal', goalName, data);
}
}

export function trackRevenue(amount: number, currency = 'USD') {
if (browser && window.zenovay) {
  window.zenovay('revenue', amount, currency);
}
}

Then use it in any component:

src/routes/pricing/+page.svelteSVELTE
<script lang="ts">
import { track } from '$lib/analytics';

function handleUpgrade(plan: string) {
  track('upgrade_clicked', { plan, location: 'pricing_page' });
}
</script>

<button on:click={() => handleUpgrade('pro')}>
Upgrade to Pro
</button>

Track Page Views in Layout

If you want to fire additional metadata with each page view, use a reactive statement in your root layout:

src/routes/+layout.svelteSVELTE
<script lang="ts">
import { page } from '$app/stores';
import { browser } from '$app/environment';

$: if (browser && $page.url) {
  // Zenovay auto-tracks page views, but you can add custom data
  if (window.zenovay) {
    window.zenovay('track', 'page_metadata', {
      path: $page.url.pathname,
      route_id: $page.route.id,
    });
  }
}
</script>

<slot />

The above is optional. Zenovay already tracks page views automatically. Use this pattern only if you need to attach extra metadata to each navigation.

Server-Side Rendering Considerations

The Zenovay script runs entirely on the client. During SSR, window is not available, so:

  • Never call window.zenovay in +page.server.ts, +layout.server.ts, or server hooks
  • Always guard client-side calls with browser from $app/environment
  • The script tag in app.html is only executed by the browser, so it is safe there
Do NOT do this in server filesTypeScript
// +page.server.ts — this will throw!
// window.zenovay('track', 'page_load'); // ❌

// Instead, track on the client side in +page.svelte
// import { browser } from '$app/environment';
// if (browser && window.zenovay) { ... } // ✅

Cookieless Mode

For privacy-friendly tracking without cookies or localStorage, add the data-cookieless attribute:

Cookieless tracking in src/app.htmlHTML
<script
defer
data-tracking-code="YOUR_TRACKING_CODE"
data-cookieless="true"
src="https://api.zenovay.com/z.js"
></script>

In cookieless mode, Zenovay uses a server-side hash of the visitor's IP subnet, user agent, and a daily-rotating salt to count unique visitors without storing anything on the client device.

Identify Users

Associate analytics data with authenticated users. Typically call this after login or in a layout that loads user data:

src/routes/+layout.svelte (authenticated)SVELTE
<script lang="ts">
import { browser } from '$app/environment';
import type { LayoutData } from './$types';

export let data: LayoutData;

$: if (browser && data.user && window.zenovay) {
  window.zenovay('identify', data.user.id, {
    email: data.user.email,
    plan: data.user.plan,
  });
}
</script>

<slot />

Track Goals and Revenue

src/lib/analytics.ts (goals and revenue)TypeScript
import { trackGoal, trackRevenue } from '$lib/analytics';

// Track a conversion goal
trackGoal('newsletter_signup', { source: 'footer' });

// Track a purchase
trackRevenue(49.99, 'USD');

TypeScript Support

Add type declarations for the global zenovay function:

src/app.d.tsTypeScript
// See https://kit.svelte.dev/docs/types#app
declare global {
namespace App {
  // interface Error {}
  // interface Locals {}
  // interface PageData {}
  // interface Platform {}
}

interface Window {
  zenovay?: (...args: any[]) => void;
}
}

export {};

Form Actions Tracking

Track SvelteKit form actions (progressive enhancement):

src/routes/contact/+page.svelteSVELTE
<script lang="ts">
import { enhance } from '$app/forms';
import { track } from '$lib/analytics';
</script>

<form
method="POST"
use:enhance={() => {
  track('form_submitted', { form: 'contact' });
  return async ({ result }) => {
    if (result.type === 'success') {
      track('form_success', { form: 'contact' });
    }
  };
}}
>
<input name="email" type="email" placeholder="Email" required />
<textarea name="message" placeholder="Message" required></textarea>
<button type="submit">Send</button>
</form>

Next Steps

Your SvelteKit app is now tracking with Zenovay! View your analytics in the dashboard.

Continue learning:

Was this page helpful?