---
url: 'https://docs.kitbase.dev/sdks/javascript.md'
description: >-
  Complete reference for the @kitbase/analytics TypeScript SDK. Track events,
  manage user identity, offline queuing, and more.
---

# JavaScript / TypeScript SDK

`@kitbase/analytics` is the core TypeScript SDK for Kitbase analytics. It is fully typed, works in both browser and Node.js environments, and provides a rich API for event tracking, user identity, session management, and offline queuing.

::: tip Two Builds Available
| Build | Import | Size | Notes |
|-------|--------|------|-------|
| **Full** | `@kitbase/analytics` | ~50KB | Includes offline queue (IndexedDB via Dexie) |
| **Lite** | `@kitbase/analytics/lite` | ~12KB | Excludes offline queue |

Use the lite build when you do not need offline support and want a smaller bundle.
:::

## Installation

::: code-group

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

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

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

:::

## Initialization

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

const kitbase = init({
  sdkKey: 'YOUR_SDK_KEY',
  debug: false,
  baseUrl: 'https://ingest.kitbase.dev',
  storage: localStorage,
  storageKey: 'kitbase_anonymous_id',
  analytics: {
    autoTrackPageViews: true,
    trackBfcacheRestore: true,
    autoTrackOutboundLinks: true,
    autoTrackClicks: true,
    autoTrackScrollDepth: true,
    autoTrackVisibility: true,
    autoTrackWebVitals: false,
    autoDetectFrustration: true,
  },
  offline: {
    enabled: false,
    maxQueueSize: 1000,
    flushInterval: 30000,
    flushBatchSize: 50,
    maxRetries: 3,
    retryBaseDelay: 1000,
  },
});
```

### Configuration Options

#### Root Options

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `sdkKey` | `string` | **required** | Your SDK key from the Kitbase dashboard |
| `debug` | `boolean` | `false` | Enable debug logging to the console |
| `baseUrl` | `string` | `'https://ingest.kitbase.dev'` | Ingestion endpoint (override for self-hosted) |
| `storage` | `Storage \| null` | `localStorage` | Storage backend for anonymous ID persistence |
| `storageKey` | `string` | `'kitbase_anonymous_id'` | Key used in storage for the anonymous ID |

#### Analytics Options

| 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 back-forward cache (bfcache). Important for MPA sites where back/forward navigation does not re-execute scripts. |
| `autoTrackOutboundLinks` | `boolean` | `true` | Automatically track clicks on external links |
| `autoTrackClicks` | `boolean` | `true` | Automatically track clicks on interactive elements |
| `autoTrackScrollDepth` | `boolean` | `true` | Automatically track maximum scroll depth per page |
| `autoTrackVisibility` | `boolean` | `true` | Track element visibility via `data-kb-track-visibility` attributes |
| `autoTrackWebVitals` | `boolean` | `false` | Track Core Web Vitals (LCP, CLS, INP, FCP, TTFB) |
| `autoDetectFrustration` | `boolean` | `true` | Detect frustration signals (rage clicks and dead clicks) |

#### Offline Options

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `enabled` | `boolean` | `false` | Enable offline queue |
| `maxQueueSize` | `number` | `1000` | Maximum number of events to queue |
| `flushInterval` | `number` | `30000` | Auto-flush interval in milliseconds |
| `flushBatchSize` | `number` | `50` | Number of events per flush batch |
| `maxRetries` | `number` | `3` | Maximum retry attempts for failed events |
| `retryBaseDelay` | `number` | `1000` | Base delay in milliseconds for exponential backoff |

***

## API Reference

### Event Tracking

#### `track(options: TrackOptions): Promise<TrackResponse>`

Track a custom event.

```typescript
const response = await kitbase.track({
  channel: 'payments',
  event: 'New Subscription',
  user_id: 'user-123',
  icon: '💰',
  notify: true,
  description: 'User subscribed to premium plan',
  tags: {
    plan: 'premium',
    cycle: 'monthly',
    amount: 9.99,
  },
});

console.log(response.id);        // "evt_abc123"
console.log(response.timestamp); // "2026-02-13T10:30:00.000Z"
```

#### `trackPageView(options?: PageViewOptions): Promise<TrackResponse>`

Track a page view manually. When `autoTrackPageViews` is enabled, page views are tracked automatically on route changes; use this method for cases where you need explicit control.

```typescript
await kitbase.trackPageView({
  path: '/products/123',
  referrer: 'https://google.com',
  title: 'Product Details',
});

// Or with defaults (uses current window.location and document.title)
await kitbase.trackPageView();
```

#### `trackRevenue(options: RevenueOptions): Promise<TrackResponse>`

Track a revenue event.

```typescript
await kitbase.trackRevenue({
  amount: 99.99,
  currency: 'USD',
  orderId: 'order-123',
  productId: 'prod-456',
  tags: { plan: 'premium' },
});
```

#### `trackOutboundLink(options: OutboundLinkOptions): Promise<TrackResponse>`

Track an outbound link click manually. When `autoTrackOutboundLinks` is enabled, external link clicks are tracked automatically; use this method when you need to track programmatic navigations.

```typescript
await kitbase.trackOutboundLink({
  url: 'https://partner-site.com',
  text: 'Visit Partner',
});
```

***

### User Identity

#### `identify(options: IdentifyOptions): Promise<void>`

Identify a user and link their anonymous activity to a known user ID. Call this immediately after login or signup. The SDK sends the current anonymous ID along with the user ID so the backend can merge the two profiles.

```typescript
await kitbase.identify({
  userId: 'user-123',
  traits: {
    email: 'user@example.com',
    name: 'Jane Doe',
    plan: 'premium',
  },
});
```

::: warning
You must explicitly call `identify()` after authentication. Kitbase does not automatically link anonymous users to identified users.
:::

#### `getAnonymousId(): string`

Get the current anonymous user ID. This is a UUID v4 that is auto-generated and persisted in storage.

```typescript
const anonymousId = kitbase.getAnonymousId();
// => "550e8400-e29b-41d4-a716-446655440000"
```

#### `getUserId(): string | null`

Get the currently identified user ID, or `null` if no user has been identified.

```typescript
const userId = kitbase.getUserId();
// => "user-123" or null
```

#### `reset(): void`

Reset the user identity. Clears the identified user ID, generates a new anonymous ID, and starts a new session.

```typescript
kitbase.reset();
```

***

### Session Management

#### `getSession(): SessionInfo`

Get the current session information.

```typescript
const session = kitbase.getSession();
// => {
//   id: 'sess_abc123',
//   startedAt: 1705321800000,
//   lastActivityAt: 1705323600000
// }
```

#### `getSessionId(): string`

Get the current session ID.

```typescript
const sessionId = kitbase.getSessionId();
// => "sess_abc123"
```

***

### Super Properties

Super properties are key-value pairs that are automatically merged into the `tags` of every event. They are useful for persistent context such as app version, environment, or user segment.

::: warning In-Memory Storage
Super properties are stored in memory and reset when the page reloads. For persistent properties, store them externally and call `register()` on initialization.
:::

#### `register(properties: Record<string, unknown>): void`

Set super properties. If a property already exists, it is overwritten.

```typescript
kitbase.register({
  app_version: '2.1.0',
  environment: 'production',
  platform: 'web',
});
```

#### `registerOnce(properties: Record<string, unknown>): void`

Set super properties only if they are not already set. Useful for values like `first_visit_date` that should never be overwritten.

```typescript
kitbase.registerOnce({
  first_seen: new Date().toISOString(),
});
```

#### `unregister(key: string): void`

Remove a single super property.

```typescript
kitbase.unregister('platform');
```

#### `getSuperProperties(): Record<string, unknown>`

Get all currently registered super properties.

```typescript
const props = kitbase.getSuperProperties();
// => { app_version: '2.1.0', environment: 'production' }
```

#### `clearSuperProperties(): void`

Remove all super properties.

```typescript
kitbase.clearSuperProperties();
```

***

### Timed Events

Start a timer before an action and automatically include the elapsed time as a `$duration` tag (in milliseconds) when the event is tracked.

#### `timeEvent(eventName: string): void`

Start timing an event. When `track()` is later called with the same event name, the `$duration` tag is automatically appended.

```typescript
kitbase.timeEvent('Checkout Completed');

// ... user completes checkout ...

await kitbase.track({
  channel: 'purchases',
  event: 'Checkout Completed',
  tags: { order_id: 'order-123' },
});
// Result: tags include { order_id: 'order-123', $duration: 45000 }
```

#### `cancelTimeEvent(eventName: string): void`

Cancel a running timer without tracking the event.

```typescript
kitbase.cancelTimeEvent('Checkout Completed');
```

#### `getTimedEvents(): string[]`

Get a list of all events that currently have active timers.

```typescript
const activeTimers = kitbase.getTimedEvents();
// => ['Checkout Completed', 'Video Watched']
```

#### `getEventDuration(eventName: string): number`

Get the elapsed time in milliseconds for a timed event without stopping the timer.

```typescript
const elapsed = kitbase.getEventDuration('Video Watched');
// => 45000
```

***

### Offline Queue

When offline mode is enabled, events are queued locally and automatically flushed when the connection is restored. See [Offline Support](#offline-support) for full details.

#### `getQueueStats(): Promise<QueueStats>`

Get statistics about the offline queue.

```typescript
const stats = await kitbase.getQueueStats();
// => { pending: 15, failed: 2, total: 17 }
```

#### `flushQueue(): Promise<void>`

Force send all queued events immediately.

```typescript
await kitbase.flushQueue();
```

#### `clearQueue(): Promise<void>`

Clear all queued events without sending them.

```typescript
await kitbase.clearQueue();
```

***

### Debug & Lifecycle

#### `setDebugMode(enabled: boolean): void`

Toggle debug logging at runtime.

```typescript
kitbase.setDebugMode(true);
```

#### `isDebugMode(): boolean`

Check whether debug mode is currently enabled.

```typescript
if (kitbase.isDebugMode()) {
  console.log('Debug mode is active');
}
```

#### `enableAutoPageViews(): void`

Enable automatic page view tracking after initialization.

```typescript
kitbase.enableAutoPageViews();
```

#### `shutdown(): Promise<void>`

Flush all pending events, clean up timers and listeners, and release resources. Call this when your application unmounts.

```typescript
await kitbase.shutdown();
```

***

## TypeScript Types

### TrackOptions

```typescript
interface TrackOptions {
  /** Category for the event (e.g., "payments", "users") */
  channel: string;
  /** Name of the event (e.g., "New Subscription") */
  event: string;
  /** Identifier for the user */
  user_id?: string;
  /** Emoji or icon name */
  icon?: string;
  /** Send a real-time notification */
  notify?: boolean;
  /** Additional context about the event */
  description?: string;
  /** Key-value metadata */
  tags?: Record<string, unknown>;
}
```

### TrackResponse

```typescript
interface TrackResponse {
  /** Unique event ID (e.g., "evt_abc123") */
  id: string;
  /** Event name */
  event: string;
  /** ISO 8601 timestamp */
  timestamp: string;
}
```

### PageViewOptions

```typescript
interface PageViewOptions {
  /** Page path (defaults to window.location.pathname) */
  path?: string;
  /** Referrer URL (defaults to document.referrer) */
  referrer?: string;
  /** Page title (defaults to document.title) */
  title?: string;
}
```

### RevenueOptions

```typescript
interface RevenueOptions {
  /** Revenue amount */
  amount: number;
  /** Currency code (e.g., "USD") */
  currency?: string;
  /** Order identifier */
  orderId?: string;
  /** Product identifier */
  productId?: string;
  /** Additional metadata */
  tags?: object;
}
```

### OutboundLinkOptions

```typescript
interface OutboundLinkOptions {
  /** Full URL of the outbound link */
  url: string;
  /** Link text content */
  text?: string;
}
```

### IdentifyOptions

```typescript
interface IdentifyOptions {
  /** Unique user identifier */
  userId: string;
  /** User properties (email, name, plan, etc.) */
  traits?: Record<string, unknown>;
}
```

### SessionInfo

```typescript
interface SessionInfo {
  /** Session ID (e.g., "sess_abc123") */
  id: string;
  /** Timestamp when the session started */
  startedAt: number;
  /** Timestamp of the last recorded activity */
  lastActivityAt: number;
}
```

### QueueStats

```typescript
interface QueueStats {
  /** Events waiting to be sent */
  pending: number;
  /** Events that failed and will be retried */
  failed: number;
  /** Total events in queue */
  total: number;
}
```

***

## Error Types

All error classes extend `KitbaseError`, which itself extends the native `Error` class. Import them directly from the package:

```typescript
import {
  KitbaseError,
  AuthenticationError,
  ValidationError,
  TimeoutError,
  ApiError,
} from '@kitbase/analytics';
```

### KitbaseError

Base class for all SDK errors.

```typescript
class KitbaseError extends Error {
  message: string;
}
```

### AuthenticationError

Thrown when the API key is invalid or missing.

```typescript
class AuthenticationError extends KitbaseError {
  message: string; // "Invalid or missing API key"
}
```

**Common causes:** incorrect `sdkKey` value, or an expired or revoked key.

### ValidationError

Thrown when a required field is missing or a field value is invalid.

```typescript
class ValidationError extends KitbaseError {
  message: string; // Error description
  field: string;   // The field that failed validation (e.g., "channel")
}
```

### TimeoutError

Thrown when a request exceeds the 30-second timeout.

```typescript
class TimeoutError extends KitbaseError {
  message: string; // "Request timed out"
}
```

### ApiError

Thrown when the API returns an error response (4xx or 5xx).

```typescript
class ApiError extends KitbaseError {
  message: string;    // Error description from API
  statusCode: number; // HTTP status code (e.g., 429, 500)
  response: unknown;  // Raw response body
}
```

### Error Handling Example

```typescript
import {
  KitbaseError,
  AuthenticationError,
  ValidationError,
  TimeoutError,
  ApiError,
} from '@kitbase/analytics';

try {
  await kitbase.track({
    channel: 'payments',
    event: 'New Subscription',
  });
} catch (error) {
  if (error instanceof AuthenticationError) {
    // Invalid API key - check your sdkKey
    console.error('Authentication failed');
  } else if (error instanceof ValidationError) {
    // Missing required field
    console.error(`Validation error on field: ${error.field}`);
  } else if (error instanceof TimeoutError) {
    // Request timed out - safe to retry
    console.error('Request timed out');
  } else if (error instanceof ApiError) {
    // API returned an error
    console.error(`API error ${error.statusCode}:`, error.response);
  } else if (error instanceof KitbaseError) {
    // Generic SDK error
    console.error(error.message);
  }
}
```

### Retry with Exponential Backoff

```typescript
import type { TrackOptions, TrackResponse, KitbaseAnalytics } from '@kitbase/analytics';
import {
  AuthenticationError,
  ValidationError,
  TimeoutError,
  ApiError,
} from '@kitbase/analytics';

async function trackWithRetry(
  kitbase: KitbaseAnalytics,
  options: TrackOptions,
  retries = 3
): Promise<TrackResponse> {
  for (let attempt = 1; attempt <= retries; attempt++) {
    try {
      return await kitbase.track(options);
    } catch (error) {
      // Never retry auth or validation errors
      if (error instanceof AuthenticationError) throw error;
      if (error instanceof ValidationError) throw error;

      const shouldRetry =
        error instanceof TimeoutError ||
        (error instanceof ApiError && error.statusCode >= 500);

      if (shouldRetry && attempt < retries) {
        const delay = Math.pow(2, attempt) * 1000; // 2s, 4s, 8s
        await new Promise((resolve) => setTimeout(resolve, delay));
        continue;
      }

      throw error;
    }
  }
  throw new Error('Max retries exceeded');
}
```

***

## Framework Examples

### React

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

// Initialize once outside of components
const kitbase = init({ sdkKey: 'YOUR_SDK_KEY' });

function App() {
  const { user } = useAuth();

  // Identify the user after authentication
  useEffect(() => {
    if (user) {
      kitbase.identify({
        userId: user.id,
        traits: {
          email: user.email,
          name: user.name,
          plan: user.plan,
        },
      });
    }
  }, [user]);

  // Cleanup on unmount
  useEffect(() => {
    return () => {
      kitbase.shutdown();
    };
  }, []);

  return <YourApp />;
}

// Track custom events anywhere in your app
function ExportButton() {
  const handleExport = async () => {
    kitbase.timeEvent('Export Generated');

    await generateExport();

    await kitbase.track({
      channel: 'engagement',
      event: 'Export Generated',
      tags: { format: 'pdf' },
    });
  };

  return <button onClick={handleExport}>Export PDF</button>;
}
```

### Next.js

```typescript
'use client';

import { init } from '@kitbase/analytics';
import { useEffect } from 'react';

// Initialize on the client side only
const kitbase =
  typeof window !== 'undefined'
    ? init({ sdkKey: 'YOUR_SDK_KEY' })
    : null;

export function AnalyticsProvider({ children }: { children: React.ReactNode }) {
  const { user } = useAuth();

  useEffect(() => {
    if (user && kitbase) {
      kitbase.identify({
        userId: user.id,
        traits: { email: user.email },
      });
    }
  }, [user]);

  useEffect(() => {
    return () => {
      kitbase?.shutdown();
    };
  }, []);

  return <>{children}</>;
}

// Export for use across your app
export { kitbase };
```

```typescript
// app/layout.tsx
import { AnalyticsProvider } from './analytics-provider';

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        <AnalyticsProvider>{children}</AnalyticsProvider>
      </body>
    </html>
  );
}
```

### Vue

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

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

export default {
  install(app) {
    app.provide('kitbase', kitbase);
    app.config.globalProperties.$kitbase = kitbase;
  },
};

export { kitbase };
```

```typescript
// main.ts
import { createApp } from 'vue';
import kitbasePlugin from './plugins/kitbase';
import App from './App.vue';

const app = createApp(App);
app.use(kitbasePlugin);
app.mount('#app');
```

```vue
<!-- components/ExportButton.vue -->
<script setup lang="ts">
import { inject } from 'vue';
import type { KitbaseAnalytics } from '@kitbase/analytics';

const kitbase = inject<KitbaseAnalytics>('kitbase')!;

async function handleExport() {
  await kitbase.track({
    channel: 'engagement',
    event: 'Export Generated',
    tags: { format: 'csv' },
  });
}
</script>

<template>
  <button @click="handleExport">Export CSV</button>
</template>
```

### Nuxt

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

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

  return {
    provide: {
      kitbase,
    },
  };
});
```

```vue
<!-- pages/index.vue -->
<script setup lang="ts">
const { $kitbase } = useNuxtApp();

async function onSignUp() {
  await $kitbase.identify({ userId: 'user-123' });
  await $kitbase.track({
    channel: 'users',
    event: 'User Signed Up',
    icon: '👤',
    notify: true,
  });
}
</script>
```

***

## Offline Support

### How It Works

When offline mode is enabled, the SDK queues events locally when the network is unavailable and automatically flushes them when the connection is restored.

```
User action
    |
track() called
    |
+------------------+
| Online?          |
+------------------+
    |          |
   Yes         No
    |          |
Send to     Queue in
  API       IndexedDB
    |          |
    |    +----------+
    |    | Network  | <-- 'online' event
    |    | restored |
    |    +----------+
    |          |
    |     Flush queue
    |          |
   Done       Done
```

### Enabling Offline Mode

```typescript
const kitbase = init({
  sdkKey: 'YOUR_SDK_KEY',
  offline: {
    enabled: true,
    maxQueueSize: 1000,
    flushInterval: 30000,
    flushBatchSize: 50,
    maxRetries: 3,
    retryBaseDelay: 1000,
  },
});
```

::: tip Full Build Required
Offline mode requires the full build (`@kitbase/analytics`). The lite build (`@kitbase/analytics/lite`) does not include offline queue support.
:::

### Retry Logic

Failed events are retried with exponential backoff. The delay is calculated as `retryBaseDelay * 2^attempt`:

| Attempt | Delay | Action |
|---------|-------|--------|
| 1st retry | 1 second | Retry |
| 2nd retry | 2 seconds | Retry |
| 3rd retry | 4 seconds | Retry |
| After max retries | -- | Event dropped |

### Storage

| Environment | Backend | Persistence |
|-------------|---------|-------------|
| Browser | IndexedDB via Dexie | Events survive page reloads and browser restarts |
| Node.js | In-memory | Events are lost on process restart |

### Queue Management

```typescript
// Check queue health
const stats = await kitbase.getQueueStats();
if (stats.pending > 0) {
  showOfflineIndicator();
}

// Force flush (e.g., before navigation)
await kitbase.flushQueue();

// Clear queue (e.g., discard pending events)
await kitbase.clearQueue();
```

### Progressive Web App Integration

```typescript
// Flush on service worker sync
self.addEventListener('sync', async (event) => {
  if (event.tag === 'kitbase-sync') {
    event.waitUntil(kitbase.flushQueue());
  }
});
```

::: warning Queue Overflow
When the queue reaches `maxQueueSize`, new events are dropped. Monitor `getQueueStats()` for critical applications.
:::

***

## Bot Filtering

Kitbase automatically filters out bot and crawler traffic. Events are not sent when your app is visited by headless browsers (Puppeteer, Playwright), automated testing tools (Selenium, WebDriver), HTTP clients (curl, wget), search engine crawlers (Googlebot, Bingbot), or social media crawlers (Facebook, Twitter, Slack). No configuration is required.

Filtering keeps bots **out** of your analytics. To **see** bot and crawler traffic instead — which AI crawlers and search engines visit your site — use the separate [Bot & Crawler Detection](/crawler-detection) feature ([detection vs. filtering](/crawler-detection#detection-vs-filtering)).

***

## Auto-Tracked Events

When auto-tracking is enabled (the default), the SDK fires the following events on the internal `__analytics` channel:

| Event | Config Flag | Trigger |
|-------|-------------|---------|
| `screen_view` | `autoTrackPageViews` | Page load, `pushState`, `popstate` |
| `screen_view` | `trackBfcacheRestore` | Page restored from bfcache (back/forward in MPAs) |
| `outbound_link` | `autoTrackOutboundLinks` | Click on external link |
| `click` | `autoTrackClicks` | Click on interactive element |
| `scroll_depth` | `autoTrackScrollDepth` | Page unload (reports max scroll %) |
| `rage_click` | `autoDetectFrustration` | 3+ clicks within 1s in same area |
| `dead_click` | `autoDetectFrustration` | Click with no DOM change within 1s |
| `web_vitals` | `autoTrackWebVitals` | Once per page load (opt-in) |
| `revenue` | -- | Explicit `trackRevenue()` call |

### Data Attributes (No-Code Tracking)

Track events from HTML without writing JavaScript by annotating elements with data attributes:

```html
<!-- Track a named click event -->
<button data-kb-track-click="CTA Clicked">Sign Up Now</button>

<!-- Track with a custom channel -->
<button
  data-kb-track-click="Add to Cart"
  data-kb-click-channel="ecommerce"
>
  Add to Cart
</button>

<!-- Track element visibility duration -->
<section data-kb-track-visibility="Pricing Section Viewed">
  ...
</section>

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

For the complete auto-tracked events reference, see [Autocapture](/autocapture).

***

## Best Practices

### Initialize Once, Reuse Everywhere

Create a single instance with `init()` and export it for use across your application. Do not create multiple instances.

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

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

```typescript
// Anywhere in your app
import { kitbase } from '@/lib/kitbase';

await kitbase.track({ channel: 'engagement', event: 'Feature Used' });
```

### Use Consistent Channel Naming

Use lowercase, plural nouns for channel names:

| Channel | Use Case |
|---------|----------|
| `payments` | Subscriptions, purchases, refunds |
| `users` | Signups, logins, profile updates |
| `errors` | Application errors, exceptions |
| `features` | Feature usage tracking |
| `engagement` | General user interactions |
| `system` | Deployments, server events |

### Leverage Auto-Tracking

Prefer auto-tracking over manual tracking wherever possible. The SDK handles page views, sessions, outbound links, clicks, and scroll depth automatically. Reserve manual `track()` calls for business-specific events like purchases, signups, and feature usage.

### Handle Errors Gracefully

Never let tracking failures break your application. Wrap calls in a safe helper:

```typescript
async function safeTrack(options: TrackOptions): Promise<void> {
  try {
    await kitbase.track(options);
  } catch (error) {
    // Log but don't throw - tracking is non-critical
    console.error('Event tracking failed:', error);
  }
}
```

### Call shutdown() on Unmount

Always call `shutdown()` when your application unmounts to ensure all queued events are sent and resources are released:

```typescript
// React
useEffect(() => {
  return () => {
    kitbase.shutdown();
  };
}, []);

// Vanilla JS
window.addEventListener('beforeunload', () => {
  kitbase.shutdown();
});
```

### Use Super Properties for Shared Context

Set properties like app version and environment once via `register()` instead of passing them on every `track()` call:

```typescript
kitbase.register({
  app_version: '2.1.0',
  environment: process.env.NODE_ENV,
});

// All subsequent events automatically include these tags
await kitbase.track({ channel: 'users', event: 'Signup' });
// tags: { app_version: '2.1.0', environment: 'production', ... }
```

### Use snake\_case for Tag Keys

```typescript
// Good
tags: { user_type: 'premium', error_code: 'E001' }

// Bad
tags: { UserType: 'premium', 'error-code': 'E001' }
```

***

## Next Steps

* [Analytics Dashboard](/guide/analytics) -- Dashboard metrics, breakdowns, and filtering
* [Autocapture](/autocapture) -- Complete reference for every automatic event
* [Custom Events](/track-events) -- Channels, tags, notifications, and use cases
* [Identify Users](/identify-users) -- Anonymous tracking and identity resolution
* [Error Handling](/error-handling) -- Full error handling reference for all SDKs
* [Analytics Guide](/guide/analytics) -- Privacy, bot filtering, and data enrichment
