> ## Documentation Index
> Fetch the complete documentation index at: https://docs.platform.embeddables.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Analytics SDK

> @embeddables/analytics — track page views, clicks, purchases, and custom events

## Overview

Analytics records visitor activity — page views, clicks, purchases, and custom events — tagged with the current project and visitor identity from [Core](/reference/core-sdk). Once tracked, that data is available for reporting and analysis in Embeddables.

## What gets tracked

| Event                    | What it means                                                                                                                |
| ------------------------ | ---------------------------------------------------------------------------------------------------------------------------- |
| `page:viewed`            | A screen or step was shown                                                                                                   |
| `button:clicked`         | A button or call-to-action was pressed                                                                                       |
| `field:updated`          | A single form answer changed (usually sent automatically by [Forms](/reference/sdks/forms-sdk))                              |
| `data:updated`           | A batch of form answers changed (usually sent automatically by [Forms](/reference/sdks/forms-sdk))                           |
| `form:submitted`         | A form was submitted (usually sent automatically by [Forms](/reference/sdks/forms-sdk))                                      |
| `payment:completed`      | A purchase completed                                                                                                         |
| `custom_event:triggered` | Any other moment worth recording, with custom properties                                                                     |
| `experiment:assigned`    | A visitor was assigned to an A/B test version (usually sent automatically by [Experiments](/reference/sdks/experiments-sdk)) |

In browsers, `page:viewed` events are automatically enriched with available marketing (UTM) parameters, device type, and a country guess.

## Implementation

<Info>
  Requires a `publishableKey` on your Core config (or passed directly to `initAnalytics`).
  Everything else in Core works without one, but Analytics authenticates every request with it.
</Info>

### Install

<CodeGroup>
  ```bash npm theme={null}
  npm install @embeddables/analytics @embeddables/core
  ```

  ```bash pnpm theme={null}
  pnpm add @embeddables/analytics @embeddables/core
  ```

  ```bash yarn theme={null}
  yarn add @embeddables/analytics @embeddables/core
  ```
</CodeGroup>

React apps also need React 18 or newer.

### Client

```typescript theme={null}
import { initEmbeddables } from '@embeddables/core'
import { initAnalytics } from '@embeddables/analytics'

const embeddables = initEmbeddables({
  projectId: '<your-project-id>',
  publishableKey: 'pk_sandbox_<your-key>',
  forms: [],
  experiments: [],
})

const analytics = initAnalytics({ core: embeddables })

await analytics.trackEvent({ event_name: 'page:viewed', page_key: 'intro' })
await analytics.trackEvent({ event_name: 'payment:completed', payment_value: 49 })
```

Analytics always reads the *current* visitor off the Core instance — if the user's identity changes after `initAnalytics`, the next `trackEvent` picks that up automatically.

Any field you set explicitly on an event overrides the value Analytics would have detected automatically.

#### `initAnalytics` options

| Option           | Default    | Purpose                                         |
| ---------------- | ---------- | ----------------------------------------------- |
| `core`           | —          | Required. An initialized Core instance          |
| `publishableKey` | Core's key | Override the publishable key just for Analytics |

<Warning>
  `initAnalytics` throws if no valid `pk_(sandbox\|live)_*` key is available from either the option
  or `core.getPublishableKey()`. Set one before calling it.
</Warning>

### React

Register Analytics through `EmbeddablesProvider`'s `modules` prop — the CLI generates the module list in `embeddables/_dist/modules` — then read the track hooks anywhere underneath. They share one client per Core instance.

```tsx theme={null}
import { EmbeddablesProvider } from '@embeddables/core/react'
import {
  useTrackClickEvent,
  useTrackCustomEvent,
  useTrackEvent,
} from '@embeddables/analytics/react'
import { config } from './embeddables/_dist'
import { modules } from './embeddables/_dist/modules'

function App({ children }) {
  return (
    <EmbeddablesProvider config={config} modules={modules}>
      {children}
    </EmbeddablesProvider>
  )
}

function Checkout() {
  const { trackEvent, isPending: isTrackingEvent, isError, error } = useTrackEvent()
  const { trackClickEvent, isPending: isTrackingClick } = useTrackClickEvent()
  const { trackCustomEvent, isPending: isTrackingCustom } = useTrackCustomEvent()

  const goToCheckout = () => {
    // navigate after the click is tracked
  }

  return (
    <>
      <button
        onClick={trackClickEvent({ key: 'buy', callback: goToCheckout })}
        disabled={isTrackingClick}
      >
        Buy
      </button>
      <button
        disabled={isTrackingEvent}
        onClick={() => void trackEvent({ event_name: 'page:viewed', page_key: 'checkout' })}
      >
        Track page view
      </button>
      <button
        disabled={isTrackingCustom}
        onClick={() => void trackCustomEvent({ properties: { key: 'promo_shown' } })}
      >
        Track custom event
      </button>
      {isError && error instanceof Error ? <p>{error.message}</p> : null}
    </>
  )
}
```

| Hook / factory          | Behavior                                                                                                                                                                               |
| ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `analytics(options?)`   | Module factory for the provider's `modules` prop; its `init(core)` creates the client and calls `core.setAnalyticsInstance`                                                            |
| `useTrackEvent()`       | `{ trackEvent, isPending, isError, error }` — track any event                                                                                                                          |
| `useTrackClickEvent()`  | `{ trackClickEvent, isPending, isError, error }`; `trackClickEvent({ key, callback })` returns an `onClick` that sends `button:clicked`, then runs `callback` after a successful track |
| `useTrackCustomEvent()` | `{ trackCustomEvent, isPending, isError, error }` — send `custom_event:triggered` with `properties`                                                                                    |

`isPending` reflects an in-flight track call, not initialization. Before Core and the Analytics module are ready, track calls are no-ops — they never throw or queue.

<Note>
  For project and visitor identity in React, use the Core hooks (`useAppUserId`,
  `useEmbeddablesProjectId`) — Analytics React bindings don't duplicate Core getters. React hooks
  never track during server rendering; track from server code through the server entry point below.
</Note>

### Server

```typescript theme={null}
import { initEmbeddablesServer } from '@embeddables/core/server'
import { initAnalyticsServer } from '@embeddables/analytics/server'

const server = initEmbeddablesServer({
  projectId: '<your-project-id>',
  publishableKey: 'pk_sandbox_<your-key>',
  forms: [],
  experiments: [],
  cookies: { get: (key) => request.cookies.get(key) ?? null },
})

const analytics = initAnalyticsServer({ server })

await analytics.trackEvent({
  event_name: 'custom_event:triggered',
  properties: { key: 'signup' },
})
```

The server client doesn't add browser context (no UTM/device/country) and only accepts `custom_event:triggered` and `experiment:assigned` — everything else is tracked from the browser.

Timestamps are assigned automatically — you never set one yourself. Each `event_name` only accepts its own matching fields, so TypeScript flags a `payment_value` on a `page:viewed` event before it ever ships.

### Wiring Analytics into Experiments or Forms

Analytics never depends on Experiments or Forms, and they never depend on it — the connection is optional. In non-React code, pass the client you already created as the `analytics` / `analyticsInstance` option:

```typescript theme={null}
const analytics = initAnalytics({ core: embeddables })

const experiments = initExperiments({ core: embeddables, analytics })
const forms = initForms({ core: embeddables, schemas, analyticsInstance: analytics })
```

In React, the CLI-generated `modules` are ordered `analytics → experiments → forms`, so Experiments and Forms read the client back off Core automatically — you don't wire it yourself. See [Experiments](/reference/sdks/experiments-sdk#analytics) and [Forms](/reference/sdks/forms-sdk#analytics) for what each one emits.
