npm package: @klime/browser
Description: Klime SDK for browsers
Author: Klime
Latest version: 1.3.0

# @klime/browser

Klime SDK for browsers.

## Installation

```bash
npm install @klime/browser
```

## Quick Start

```javascript
import { KlimeClient } from "@klime/browser";

const client = new KlimeClient({
  writeKey: "your-write-key",
});

// Identify a user
client.identify("user_123", {
  email: "user@example.com",
  name: "Stefan",
});

// Track an event
client.track(
  "Button Clicked",
  {
    buttonName: "Sign up",
    plan: "pro",
  },
  { userId: "user_123" }
);

// Associate user with a group and set group traits
client.group(
  "org_456",
  { name: "Acme Inc", plan: "enterprise" },
  { userId: "user_123" }
);

// Or just link the user to a group (if traits are already set)
client.group("org_456", null, { userId: "user_123" });
```

## Good to know

- **No anonymous tracking.** Every `track()` call needs a `userId` or `groupId`. Wait until the user is identified before sending events.
- **Name your events in past tense** with an Object + Action pattern: `"Report Generated"`, `"User Invited"`, `"Export Completed"`.
- **Always set `email` and `name` traits** in `identify()` and `group()`. These are used for display and search in the dashboard.
- **Traits can be strings, numbers, booleans, or ISO 8601 date strings.** Use camelCase for trait keys.
- **In Group mode, your dashboard stays empty until you call `group()`.** Events from `identify()` and `track()` are recorded, but customers won't appear until users are linked to groups.
- **Order doesn't matter.** Events sent before `identify()` or `group()` are retroactively attributed once the relationships are established.

## Installation Prompt

Copy and paste this prompt into Cursor, Copilot, or your favorite AI editor to integrate Klime:

```
Integrate Klime for customer analytics. Klime tracks user activity to identify which customers are healthy vs at risk of churning.

ANALYTICS MODES (determine which applies):
- Companies & Teams: Your customers are companies with multiple team members (SaaS, enterprise tools)
  → Use identify() + group() + track()
- Individual Customers: Your customers are individuals with private accounts (consumer apps, creator tools)
  → Use identify() + track() only (no group() needed)

KEY CONCEPTS:
- Every track() call requires either userId OR groupId (no anonymous events)
- Use groupId alone for org-level events (webhooks, cron jobs, system metrics)
- group() links a user to a company AND sets company traits (only for Companies & Teams mode)
- Order doesn't matter - events before identify/group still get attributed correctly

BEST PRACTICES:
- Initialize client ONCE (singleton, React context, or module-level)
- Write key is safe to embed in client code (it's write-only, not a secret)
- Events auto-flush on page unload - no manual shutdown needed

Install: npm install @klime/browser

import { KlimeClient } from "@klime/browser";

const client = new KlimeClient({ writeKey: "YOUR_WRITE_KEY" });

// Identify users at signup/login:
client.identify("usr_abc123", { email: "jane@acme.com", name: "Jane Smith" });

// Track key activities:
client.track("Report Generated", { report_type: "revenue" }, { userId: "usr_abc123" });
client.track("Feature Used", { feature: "export", format: "csv" }, { userId: "usr_abc123" });
client.track("Teammate Invited", { role: "member" }, { userId: "usr_abc123" });

// If Companies & Teams mode: link user to their company and set company traits
client.group("org_456", { name: "Acme Inc", plan: "enterprise" }, { userId: "usr_abc123" });

INTEGRATION WORKFLOW:

Phase 1: Discover
Explore the codebase to understand:
1. What framework is used? (React, Vue, Svelte, vanilla JS, etc.)
2. Where is user identity available? (e.g., useUser().id, useAuth().user.id, session.user.id, store.user)
3. Is this Companies & Teams or Individual Customers?
   - Look for: organization, workspace, tenant, team, account in URL/context/store → Companies & Teams (use group())
   - No company/org concept, just individual users → Individual Customers (skip group())
4. Where do core user actions happen? (onClick handlers, form submissions, hooks, event handlers)
5. Is there existing analytics? (search: segment, posthog, mixpanel, amplitude, .track)
Match your integration style to the framework's conventions.

Phase 2: Instrument
Add these calls using idiomatic patterns for the framework:
- Initialize client once (React: context provider, Vue: plugin, Svelte: store, vanilla: module singleton)
- identify() in auth/login success handler
- group() when user-org association is established (Companies & Teams mode only)
- track() for key user actions (see below)

WHAT TO TRACK:
Active engagement (primary): feature usage, resource creation, collaboration, completing flows
Session signals (secondary): dashboard access, app opened - distinguishes "low usage" from "churned"
Do NOT track: every click, page navigations, component renders

Phase 3: Verify
Confirm: client initialized, identify/group/track calls added

Phase 4: Summarize
Report what you added:
- Files modified and what was added to each
- Events being tracked (list event names and what triggers them)
- How userId is obtained (and groupId if Companies & Teams mode)
- Any assumptions made or questions
```

## API Reference

### Constructor

```typescript
new KlimeClient(config: {
  writeKey: string;              // Required: Your Klime write key
  endpoint?: string;              // Optional: API endpoint (default: https://i.klime.com)
  flushInterval?: number;        // Optional: Milliseconds between flushes (default: 2000)
  maxBatchSize?: number;         // Optional: Max events per batch (default: 20, max: 100)
  maxQueueSize?: number;         // Optional: Max queued events (default: 1000)
  retryMaxAttempts?: number;     // Optional: Max retry attempts (default: 5)
  retryInitialDelay?: number;    // Optional: Initial retry delay in ms (default: 1000)
  autoFlushOnUnload?: boolean;   // Optional: Auto-flush on page unload (default: true)
})
```

### Methods

#### `track(event: string, properties?: object, options?: { userId?, groupId? })`

Track an event. Events can be attributed in two ways:

- **User events**: Provide `userId` to track user activity (most common)
- **Group events**: Provide `groupId` without `userId` for organization-level events

```javascript
// User event (most common)
client.track(
  "Button Clicked",
  {
    buttonName: "Sign up",
    plan: "pro",
  },
  { userId: "user_123" }
);

// Group event (for webhooks, cron jobs, system events)
client.track(
  "Events Received",
  {
    count: 100,
    source: "webhook",
  },
  { groupId: "org_456" }
);
```

> **Note**: The `groupId` option can also be combined with `userId` for multi-tenant scenarios where you need to specify which organization context a user event occurred in.

#### `identify(userId: string, traits?: object)`

Identify a user with traits.

```javascript
client.identify("user_123", {
  email: "user@example.com",
  name: "Stefan",
});
```

#### `group(groupId: string, traits?: object, options?: { userId? })`

Associate a user with a group and/or set group traits.

```javascript
// Associate user with a group and set group traits (most common)
client.group(
  "org_456",
  { name: "Acme Inc", plan: "enterprise" },
  { userId: "user_123" }
);

// Just link a user to a group (traits already set or not needed)
client.group("org_456", null, { userId: "user_123" });

// Just update group traits (e.g., from a webhook or background job)
client.group("org_456", { plan: "enterprise", employeeCount: 50 });
```

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

Manually flush queued events immediately.

```javascript
await client.flush();
```

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

Gracefully shutdown the client, flushing remaining events.

```javascript
await client.shutdown();
```

## Features

- **Automatic Batching**: Events are automatically batched and sent every 2 seconds or when the batch size reaches 20 events
- **Automatic Retries**: Failed requests are automatically retried with exponential backoff
- **Browser Context**: Automatically captures userAgent, locale, and timezone
- **Page Unload Handling**: Flushes on `visibilitychange` (when tab is hidden), `pagehide`, and `beforeunload`, and uses `fetch` with `keepalive: true` (when batch ≤64KB) so requests can complete after the page unloads
- **Zero Dependencies**: Uses only native browser APIs
- **Universal Module Support**: Works with ESM and CommonJS (no `transpilePackages` needed)

## Module Compatibility

This package ships dual ESM/CommonJS builds and works out of the box with:

- **Bundlers**: Vite, webpack, esbuild, Rollup, Parcel
- **Frameworks**: Next.js, Remix, Nuxt, SvelteKit, Astro
- **Runtimes**: Browsers, Node.js (SSR), Deno, Bun

```javascript
// ESM (recommended)
import { KlimeClient } from "@klime/browser";

// CommonJS
const { KlimeClient } = require("@klime/browser");
```

No special configuration required — it just works.

## Performance

When you call `track()`, `identify()`, or `group()`, the SDK:

1. Adds the event to an in-memory queue (microseconds)
2. Returns immediately without waiting for network I/O

Events are sent to Klime's servers asynchronously via the browser's `fetch` API. This means:

- **No network waiting**: HTTP requests happen asynchronously in the background
- **No UI blocking**: Tracking calls don't freeze the browser or delay user interactions
- **Automatic batching**: Events are queued and sent in batches (default: every 2 seconds or 20 events)

```javascript
// This returns immediately - no HTTP request is made here
client.track("Button Clicked", { button: "signup" }, { userId: "user_123" });

// Your code continues without waiting
navigate("/dashboard");
```

The only blocking operation is `await flush()`, which waits for all queued events to be sent. The SDK automatically flushes on page unload (and uses `keepalive` so the request can complete after the page is gone).

**SPAs (e.g. Next.js)**: Client-side route changes do not trigger `beforeunload` or `pagehide`. To avoid losing events on navigation, call `flush()` when the route changes (e.g. in your router's `onRouteChange` or in a `useEffect` that depends on `pathname`).

## Configuration

### Default Values

- `flushInterval`: 2000ms
- `maxBatchSize`: 20 events
- `maxQueueSize`: 1000 events
- `retryMaxAttempts`: 5 attempts
- `retryInitialDelay`: 1000ms
- `autoFlushOnUnload`: true

## Error Handling

The SDK automatically handles:

- **Transient errors** (429, 503, network failures): Retries with exponential backoff
- **Permanent errors** (400, 401): Logs error and drops event
- **Rate limiting**: Respects `Retry-After` header

## Size Limits

- Maximum event size: 200KB
- Maximum batch size: 10MB
- Maximum events per batch: 100

Events exceeding these limits are rejected and logged.

## License

MIT