---
url: 'https://docs.kitbase.dev/tracking-script.md'
description: >-
  Add Kitbase analytics to any website with a lightweight tracking script.
  Privacy-friendly, no cookies, works on static sites, SPAs, and SSR.
---

# Tracking Script

Kitbase provides a lightweight tracking script for web analytics and event tracking. It is privacy-friendly, requires no cookies by default, and works on any website -- static HTML, single-page applications (SPAs), and server-side rendered (SSR) frameworks alike.

## Installation

### Script Tag (Simplest)

Add these two snippets to your HTML `<head>` section:

```html
<script>
  window.KITBASE_CONFIG = { sdkKey: 'YOUR_SDK_KEY' };
</script>
<script defer src="https://kitbase.dev/lite.js"></script>
```

The `KITBASE_CONFIG` object **must** be set before the tracking script loads. The `defer` attribute ensures the script is fetched in parallel and executed after the HTML is parsed, so it never blocks page rendering.

### NPM Package

For JavaScript/TypeScript projects with a build step, install the SDK as a dependency:

::: code-group

```bash [pnpm]
pnpm add @kitbase/analytics
```

```bash [npm]
npm install @kitbase/analytics
```

```bash [yarn]
yarn add @kitbase/analytics
```

:::

Then initialize the SDK in your application entry point:

```typescript
import { init } from '@kitbase/analytics';

const kitbase = init({
  sdkKey: 'YOUR_SDK_KEY',
  analytics: {
    autoTrackPageViews: true,
    trackBfcacheRestore: true,
    autoTrackOutboundLinks: true,
    autoTrackClicks: true,
    autoTrackScrollDepth: true,
    autoTrackVisibility: true,
  },
});
```

::: tip Lite Build
For a smaller bundle size (~12KB vs ~50KB), use the lite build which excludes offline queue support:

```typescript
import { init } from '@kitbase/analytics/lite';
```

The lite build has the same API surface for analytics and event tracking. Only the offline queue module (`offline` config) is removed.
:::

## Configuration Reference

Below is the full configuration object with every available option and its default value.

```typescript
const kitbase = init({
  // ── Core ───────────────────────────────────────────────
  sdkKey: 'YOUR_SDK_KEY',               // Required. SDK API key from the dashboard.
  debug: false,                         // Enable console logging for troubleshooting.
  baseUrl: 'https://ingest.kitbase.dev', // Ingestion endpoint. Override for self-hosted.
  storage: localStorage,                // Storage backend for the anonymous ID.
                                        // Set to `null` to disable persistence.
  storageKey: 'kitbase_anonymous_id',   // Key used in the storage backend.

  // ── Analytics ──────────────────────────────────────────
  analytics: {
    autoTrackPageViews: true,           // Fire `screen_view` on every navigation.
    trackBfcacheRestore: true,          // Fire `screen_view` on bfcache restore (back/forward in MPAs).
    autoTrackOutboundLinks: true,       // Fire `outbound_link` for external link clicks.
    autoTrackClicks: true,              // Fire `click` for interactive element clicks.
    autoTrackScrollDepth: true,         // Fire `scroll_depth` when the user leaves a page.
    autoTrackVisibility: true,          // Observe `data-kb-track-visibility` elements.
  },

  // ── Offline Queue ──────────────────────────────────────
  offline: {
    enabled: false,                     // Queue events when offline, sync when back.
    maxQueueSize: 1000,                 // Maximum events held in the queue.
    flushInterval: 30000,               // Flush interval in milliseconds (30 s).
    flushBatchSize: 50,                 // Events sent per batch.
    maxRetries: 3,                      // Retry attempts before an event is dropped.
    retryBaseDelay: 1000,               // Base delay for exponential backoff (1 s).
  },
});
```

### Configuration Options Summary

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `sdkKey` | `string` | -- | **Required.** SDK API key from your dashboard. |
| `debug` | `boolean` | `false` | Enable verbose console logging. |
| `baseUrl` | `string` | `'https://ingest.kitbase.dev'` | Ingestion endpoint. Override for self-hosted deployments. |
| `storage` | `Storage \| null` | `localStorage` | Storage backend for anonymous ID persistence. Set to `null` to disable. |
| `storageKey` | `string` | `'kitbase_anonymous_id'` | Key name used in the storage backend. |

#### `analytics`

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `autoTrackPageViews` | `boolean` | `true` | Automatically track page views on navigation. |
| `trackBfcacheRestore` | `boolean` | `true` | Track a pageview when the browser restores a page from the back-forward cache (bfcache). Important for MPA sites where back/forward navigation does not re-execute scripts. |
| `autoTrackOutboundLinks` | `boolean` | `true` | Track clicks on links to external domains. |
| `autoTrackClicks` | `boolean` | `true` | Track clicks on interactive elements. |
| `autoTrackScrollDepth` | `boolean` | `true` | Report maximum scroll depth when the user leaves. |
| `autoTrackVisibility` | `boolean` | `true` | Observe elements with `data-kb-track-visibility`. |

#### `offline`

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `enabled` | `boolean` | `false` | Enable the offline event queue. |
| `maxQueueSize` | `number` | `1000` | Maximum events to hold in the queue. |
| `flushInterval` | `number` | `30000` | Interval between automatic flushes (ms). |
| `flushBatchSize` | `number` | `50` | Events sent per flush batch. |
| `maxRetries` | `number` | `3` | Retry attempts before an event is dropped. |
| `retryBaseDelay` | `number` | `1000` | Base delay for exponential backoff (ms). |

## What the Script Does (Behind the Scenes)

When the tracking script loads, it performs the following sequence of actions automatically. Understanding this lifecycle helps you debug issues and reason about the data you see in the dashboard.

### 1. Generates an Anonymous ID

A UUID v4 is generated and stored in `localStorage` under the key `kitbase_anonymous_id`. This ID persists across page loads so the same browser is recognized as the same visitor without cookies. If `storage` is set to `null`, the ID is regenerated on every page load.

### 2. Starts a Session

A `session_start` event is fired with a new session ID (UUID v4). The session remains active as long as events arrive within 30 minutes of inactivity. After 30 minutes of inactivity, the next interaction starts a fresh session.

### 3. Listens for Page Navigations

The SDK patches `history.pushState` and listens for the `popstate` event. This means route changes in single-page applications are detected automatically -- no framework-specific code is needed.

### 4. Tracks Page Views

A `screen_view` event is fired on the initial page load and on every subsequent navigation (pushState or popstate). Each event includes the page path, document title, referrer, and any UTM parameters present in the URL.

### 5. Monitors Clicks on Interactive Elements

A global click listener watches for clicks on interactive elements: `<a>`, `<button>`, `<input>`, `<select>`, `<textarea>`, elements with ARIA roles (`button`, `link`, `menuitem`, `tab`), and custom elements (any tag name containing a hyphen). The SDK uses `composedPath()` to traverse Shadow DOM boundaries, so web components are fully supported.

### 6. Tracks Outbound Links

When a user clicks (or keyboard-activates with Enter/Space) a link pointing to an external domain, an `outbound_link` event is fired with the full URL and the link text. Outbound link clicks are not double-counted by the generic click tracker.

### 7. Measures Scroll Depth

The SDK tracks the maximum scroll depth reached on each page. A `scroll_depth` event with a percentage (0--100) is flushed when the user navigates away (`beforeunload`, `pushState`, `popstate`). Scroll tracking uses `requestAnimationFrame` throttling to avoid scroll jank.

### 8. Observes Element Visibility

Elements annotated with `data-kb-track-visibility` are observed using `IntersectionObserver`. When such an element leaves the viewport (or is removed from the DOM, or the user navigates away), an event is fired reporting how long the element was visible. A `MutationObserver` picks up dynamically added and removed elements.

### 9. Filters Bots Automatically

Before sending any events, the SDK checks for signals that indicate non-human traffic. Events are silently discarded when the page is loaded by:

* **Headless browsers** -- Puppeteer, Playwright, PhantomJS
* **Testing tools** -- Selenium, WebDriver
* **HTTP clients** -- curl, wget, Postman
* **Search engine crawlers** -- Googlebot, Bingbot, Yandex, Baidu
* **Social media crawlers** -- Facebook, Twitter/X, Slack, LinkedIn

No configuration is required. Your analytics will only include real user traffic.

Filtering keeps bots **out** of your analytics. To **see** bot and crawler traffic instead, use the separate [Bot & Crawler Detection](/crawler-detection) feature ([detection vs. filtering](/crawler-detection#detection-vs-filtering)).

### 10. Enriches Data Server-Side

The raw events sent by the browser contain minimal data. The Kitbase backend enriches every event with:

| Enrichment | Source | Fields |
|------------|--------|--------|
| User agent parsing | `User-Agent` header | Device type, browser, browser version, OS, OS version, brand, model |
| IP geolocation | Client IP address | Country, region, city |
| UTM parameters | URL query string | `utm_source`, `utm_medium`, `utm_campaign`, `utm_term`, `utm_content` |

These enriched fields are stored as dedicated columns and available as first-class filter dimensions in the dashboard.

## Accessing the SDK After Load (Script Tag)

When using the script tag method, the SDK instance is exposed on `window.kitbase` after the script finishes loading.

### Tracking Custom Events

```javascript
window.kitbase.track({
  channel: 'engagement',
  event: 'Button Clicked',
  tags: { button_id: 'cta-header' },
});
```

### Identifying Users

```javascript
window.kitbase.identify({
  userId: 'user_123',
  traits: {
    email: 'user@example.com',
    name: 'Jane Doe',
    plan: 'premium',
  },
});
```

### Tracking Revenue

```javascript
window.kitbase.trackRevenue({
  amount: 49.99,
  currency: 'USD',
  orderId: 'order-123',
});
```

::: tip Waiting for the Script
Because the tracking script uses `defer`, it is guaranteed to execute after the DOM is parsed. If you need to call SDK methods from an inline script, use `DOMContentLoaded`:

```html
<script>
  document.addEventListener('DOMContentLoaded', function () {
    window.kitbase.track({
      channel: 'engagement',
      event: 'Page Loaded',
    });
  });
</script>
```

:::

## Data Attribute Events

The SDK supports declarative event tracking through HTML data attributes. No JavaScript is required -- annotate elements and the SDK handles the rest.

### Track Clicks

Add `data-kb-track-click` to fire a named event when the element is clicked:

```html
<button data-kb-track-click="CTA Clicked">Sign Up Now</button>

<!-- With a custom channel (default: "engagement") -->
<button
  data-kb-track-click="Add to Cart"
  data-kb-click-channel="ecommerce"
>
  Add to Cart
</button>
```

When an element has `data-kb-track-click`, the generic `click` auto-tracking is skipped for that click to avoid double-counting.

### Track Visibility

Add `data-kb-track-visibility` to measure how long a section is visible:

```html
<section data-kb-track-visibility="Pricing Section Viewed">
  ...
</section>

<!-- With custom channel and threshold -->
<div
  data-kb-track-visibility="Hero Banner Viewed"
  data-kb-visibility-channel="marketing"
  data-kb-visibility-threshold="0.75"
>
  ...
</div>
```

| Attribute | Required | Default | Description |
|-----------|----------|---------|-------------|
| `data-kb-track-visibility` | Yes | -- | Event name to fire. |
| `data-kb-visibility-channel` | No | `'engagement'` | Channel for the event. |
| `data-kb-visibility-threshold` | No | `0.5` | IntersectionObserver threshold (0--1). |

## Content Security Policy (CSP)

If your site uses a Content Security Policy, add the following directives to allow the tracking script and its API calls:

```
script-src 'self' https://kitbase.dev;
connect-src 'self' https://ingest.kitbase.dev;
```

## Subresource Integrity (SRI)

For additional security, you can verify the script has not been tampered with by using Subresource Integrity:

```html
<script
  src="https://kitbase.dev/lite.js"
  integrity="sha384-..."
  crossorigin="anonymous"
  defer
></script>
```

Contact support for the current SRI hash, or compute it yourself against the hosted script.

## Framework Integration Tips

### React / Next.js

Use the NPM package. Initialize the SDK in your root layout or `App` component so it runs once on mount:

```tsx
// app/layout.tsx (Next.js App Router) or _app.tsx (Pages Router)
import { init } from '@kitbase/analytics';

const kitbase = init({ sdkKey: 'YOUR_SDK_KEY' });
```

### Vue / Nuxt

Use the NPM package. Initialize in a plugin or in `app.vue`:

```typescript
// plugins/kitbase.client.ts (Nuxt 3)
import { init } from '@kitbase/analytics';

export default defineNuxtPlugin(() => {
  const kitbase = init({ sdkKey: 'YOUR_SDK_KEY' });
  return { provide: { kitbase } };
});
```

### Angular

Use the NPM package. Initialize in an Angular service and inject it where needed. See the [Angular SDK documentation](/sdks/angular) for a full guide.

```typescript
// kitbase.service.ts
import { Injectable } from '@angular/core';
import { init } from '@kitbase/analytics';

@Injectable({ providedIn: 'root' })
export class KitbaseService {
  public readonly client = init({ sdkKey: 'YOUR_SDK_KEY' });
}
```

### Static Sites

Use the script tag method in your HTML `<head>`. No build step required:

```html
<!DOCTYPE html>
<html>
<head>
  <script>
    window.KITBASE_CONFIG = { sdkKey: 'YOUR_SDK_KEY' };
  </script>
  <script defer src="https://kitbase.dev/lite.js"></script>
</head>
<body>
  <!-- Your site content -->
</body>
</html>
```

## Next steps

* [Proxy / Custom Domain](/proxy) -- Route requests through your own domain to bypass ad blockers
* [Analytics](/guide/analytics) -- Pageviews, sessions, bounce rate, and revenue tracking
* [Autocapture](/autocapture) -- Full reference of every automatic event and its payload
* [Custom Events](/track-events) -- Track events with channels, tags, and notifications
* [Identify Users](/identify-users) -- Anonymous tracking and identity resolution
* [JavaScript SDK](/sdks/javascript) -- Offline support and advanced features
