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

# View Events

> Capture events from inside your MCP App or ChatGPT App view with the React provider.

### Overview

[Custom events](/analytics/custom-events) let your server record what happens inside a tool call. View events are the other half: they capture what happens in the UI your tools render, the [MCP App / ChatGPT App view](https://modelcontextprotocol.io/extensions/apps/overview) shown in the client. The `@alpic-ai/insights/react` provider captures a baseline of view activity on its own, and lets you record your own events with a hook.

Both land on the same session timeline as your server-side events, so a session reads end to end: the tool call that opened the view, what the user did in it, and any error along the way.

There are no keys to configure. The provider discovers where to send events from the metadata Alpic stamps onto each tool result, and ships them best-effort, so a delivery failure never affects the host view.

### 1. Install the package

```bash theme={null}
pnpm add @alpic-ai/insights
```

The React entry point is shipped under `@alpic-ai/insights/react`. It expects `react` as a peer dependency, which your view already has.

### 2. Wrap your view

Render `AlpicAnalytics` once at the root of your view, above everything that captures events:

```tsx theme={null}
import { AlpicAnalytics } from "@alpic-ai/insights/react";
import { createRoot } from "react-dom/client";

import { App } from "./App";

createRoot(document.getElementById("root")!).render(
  <AlpicAnalytics>
    <App />
  </AlpicAnalytics>,
);
```

That is all the setup required. Events captured before the ingest endpoint is known are buffered and flushed automatically once it arrives.

### 3. Capture events

Call `useAnalytics()` from any component under the provider and use `capture` to record an event:

```tsx theme={null}
import { useAnalytics } from "@alpic-ai/insights/react";

function BookButton({ from, to }: { from: string; to: string }) {
  const analytics = useAnalytics();

  return (
    <button type="button" onClick={() => analytics.capture("flight_booked", { properties: { from, to } })}>
      Book
    </button>
  );
}
```

`capture(name, options)` takes the event name plus optional details:

| Option       | Type                      | Description                                                                            |
| ------------ | ------------------------- | -------------------------------------------------------------------------------------- |
| `message`    | `string`                  | A human-readable line shown with the event. Max 500 characters.                        |
| `duration`   | `number`                  | How long the operation took, in milliseconds. Fractional values are fine.              |
| `properties` | `Record<string, unknown>` | Freeform structured detail. Max 8 KB serialized.                                       |
| `isError`    | `boolean`                 | Marks the event as an error on the timeline.                                           |
| `error`      | `string`                  | An error description. Setting it also marks the event as an error. Max 500 characters. |

<Note>
  Event names starting with `$` are reserved for the events the SDK captures itself (see below). Use plain names for
  your own events.
</Note>

### Auto-captured events

The provider captures a baseline of activity without any `capture` calls. Each surface is gated by the `autoCapture` prop:

| Surface        | Default | Events                                                                                                       |
| -------------- | ------- | ------------------------------------------------------------------------------------------------------------ |
| `lifecycle`    | On      | `$loaded` when the view mounts, `$visible` / `$hidden` as it is shown or hidden, `$closed` when it goes away |
| `errors`       | On      | `$error` for every uncaught error and unhandled promise rejection, with the message, stack, and source       |
| `interactions` | Off     | A click event for any element carrying a `data-alpic-event` attribute (see below)                            |

Toggle any surface with the prop. Lifecycle and errors are on by default; interactions are opt-in:

```tsx theme={null}
<AlpicAnalytics autoCapture={{ interactions: true, errors: false }}>
  <App />
</AlpicAnalytics>
```

#### Declarative interaction capture

With `interactions` enabled, add `data-alpic-event` to any element to capture its clicks. The attribute value is the event name; every other `data-alpic-*` attribute becomes a string property:

```tsx theme={null}
<button data-alpic-event="cta_clicked" data-alpic-plan="pro">
  Upgrade
</button>
```

A click on that button captures `cta_clicked` with `{ plan: "pro" }`. Capture is delegated from a single `document` listener and resolves the nearest matching ancestor, so it works for elements added after mount.

### Transform or drop events with `beforeSend`

Pass `beforeSend` to inspect, rewrite, or drop each event before it is buffered. Return the event to keep it, a modified event to change it, or `null` to drop it. It runs synchronously for every event, whether captured by you or by auto-capture:

```tsx theme={null}
<AlpicAnalytics
  beforeSend={(event) => {
    if (!hasConsent) {
      return null;
    }
    return { ...event, properties: { ...event.properties, appVersion } };
  }}
>
  <App />
</AlpicAnalytics>
```

`beforeSend` is read live on every event, so deriving it from state (a consent flag, say) takes effect without remounting the provider.

### Good to know

* The provider is client-only: it discovers the ingest endpoint from the tool-result metadata and needs no API key.
* Delivery is best-effort. Failures are swallowed so analytics never affects the view; events are flushed when the view is hidden or unloaded, and on a short interval otherwise.
* At most 100 events are buffered at a time; once full, further events are dropped with a single console warning.
* View events follow the same [privacy settings](/analytics/privacy) and [plan retention](/pricing/plans) as the rest of your analytics. Do not put end-user PII in event names or properties unless you intend to collect it.
