# Events (/docs/guides/events)



An **event** is a signal your website sends to Improve when a visitor does something meaningful: viewing a page, clicking a button, signing up, or completing a purchase. Events are how Improve knows whether a test is working: by counting the visitors who reach your goal, per variant.

Events are **test-independent**: an event just records that a visitor (an anonymous visitor id) did something. Improve attributes it to a test (and to the **variant** that visitor saw) server-side, by joining the event against the visitor's **exposures**. That's what lets Improve compare variants fairly.

## How events flow [#how-events-flow]

```
Your website  →  Improve  →  Events list  →  attached to a test  →  Results
```

1. **Your site fires an event** when something happens (e.g. a visitor clicks "Buy now"). This is done by a developer with the [SDK](/docs/sdk), or (once it's set up) automatically from [Google Tag Manager](/docs/integrations/gtm).
2. **Improve records it** and, the first time it sees a new event name, **adds it to your Events list automatically**. You never have to pre-register events.
3. **You attach events to a test** to define what counts as a start, a step, and a conversion.
4. **Improve calculates results** (conversion rate and the funnel) for each variant.

## Default: track page views like Google Analytics [#default-track-page-views-like-google-analytics]

Before setting up anything else, wire up one event on every page: a GA4-style `page_view`, fired once per page load, with the current path as its `scope`. This is the single most useful event to have flowing: it doubles as the default **start event** for new tests, and it's what powers the **Unique visitors per day** chart and **Popular pages** overview on the [Events page](/events).

```tsx title="TrackPageView.tsx"
'use client'

import { useEffect } from 'react'
import { usePathname } from 'next/navigation'
import { usePostAnalytic, useTestValue } from './improve'

export const TrackPageView = () => {
	const postAnalytic = usePostAnalytic()
	const pathname = usePathname()

	useEffect(() => {
		postAnalytic('page_view', pathname) // pathname is the required scope
	}, [pathname, postAnalytic])

	return null
}
```

Mount `<TrackPageView />` once, near the root of your app inside the `ImproveProvider` (see the [React](/docs/sdk/react) / [Next.js](/docs/examples/nextjs) SDK docs), so it fires on every route without any per-page setup. If a test also reads a variant on the same page (`useTestValue` / `useFlagValue`), this `page_view` is attributed to it automatically through that exposure, no extra wiring needed.

<Callout type="info">
  Popular pages reads the page path from the `scope` on `page_view` events. If
  you already send a custom pageview event under a different name, rename it to
  `page_view` (or add this one alongside it) to light up that report.
</Callout>

## Exposure: how events get attributed to a test [#exposure-how-events-get-attributed-to-a-test]

Because events are test-independent, Improve needs a separate signal to know which test and variant a visitor belongs to. That signal is an **exposure**: the SDK emits one automatically the first time a visitor **reads a variant**, namely a `getTestValue` / `useTestValue` (or `getFlagValue` / `useFlagValue`) call that resolves a non-holdout value. The server then attributes that visitor's subsequent events to the test and variant they were exposed to.

The practical consequence: **a visitor must read a test's variant for their events to count toward that test.** Visitors who are excluded by the test's audience, or who fall into the allocation holdout, are never exposed, so their events aren't attributed to the test. Make sure the code path that fires your events also reads the variant.

## The Events list [#the-events-list]

Open the [Events page](/events) in the dashboard to see every event Improve has received for the current organisation and environment. They're grouped so you can tell what's live:

* **Recent events**: seen in the last 24 hours (these are active and safe to build tests on).
* **Inactive events**: seen before, but not recently.
* **Untracked events**: names that exist (for example, attached to a test) but haven't been received yet. If a brand-new event stays here, it usually means it isn't firing on your site yet.

<Callout type="info">
  Don't see your event? Trigger it on your site once and refresh; new event
  names appear here automatically the first time they're received.
</Callout>

## Setting up events on a test [#setting-up-events-on-a-test]

When you [create or edit a test](/tests/create), the **Events** section defines the conversion funnel (the ordered journey you want visitors to take). Each step is an **event + scope pair** — both required, e.g. `page_view` + `homepage`:

* **Start event**: the entry point that puts a visitor "into" the test, e.g. `page_view` scoped to `homepage`. This is your 100% baseline.
* **Metric events**: optional in-between steps, in order, e.g. `add_to_cart` scoped to `product_page`, then `begin_checkout` scoped to `checkout`. Each one becomes a step in the funnel.
* **Conversion event**: the final goal, e.g. `purchase` scoped to `checkout`.

Only events matching **both** the event name and its scope count toward a step — that's what lets the same event name mean different things in different places (a `button_click` scoped to `homepage_hero_login` is a different funnel step from one scoped to `homepage_hero_register`). The start step is the exception: it always counts every visitor exposed to the test, regardless of what fires afterward.

As you type the event name, Improve suggests names from your Events list; the scope field then suggests scopes actually seen for that event name, so you can pick combinations you know are firing.

<Callout type="info">
  You don't have to use metric steps. A simple test just needs a **start** and a
  **conversion** event.
</Callout>

## Conversions and the funnel [#conversions-and-the-funnel]

The **conversion rate** is the share of visitors who reach your goal (conversions ÷ starts), calculated separately for each variant so you can see which one performs best.

When you add metric steps, the test **Monitor** also shows a **multi-step funnel per variant**: the percentage of visitors who continue from one step to the next. For example:

```
page_view (homepage) → add_to_cart (product_page) 10% → begin_checkout (checkout) 30% → purchase (checkout) 5%
```

Read that as: of everyone who started, 10% added to cart; of those, 30% reached checkout; of those, 5% purchased. Comparing the same funnel across variants shows you exactly *where* one variant pulls ahead or drops off.

## Revenue and order value [#revenue-and-order-value]

Events can carry a numeric **value**: typically an order total on your conversion event. Pass it (with a currency) in the payload to [`postAnalytic`](/docs/sdk/javascript#postanalytic):

```ts
postAnalytic('purchase', 'checkout', { value: 72.05, currency: 'USD' })
```

When a test's events carry a value, the **Monitor** shows **total revenue and average order value per variant** alongside the conversion rate, so you can catch a variant that converts more often but at a lower order value, or vice versa. Values are summed as-is, so use a single currency per test.

## Naming tips [#naming-tips]

Every event is an **event name + scope** pair — keep the action generic and reusable in the name, and put the specifics in the scope. That split is what lets one `button_click` event mean "the login button on the homepage hero" in one place and "the register button" in another, instead of needing a new event name for every button.

### Event names [#event-names]

* **Start with `page_view`**: see [Default: track page views](#default-track-page-views-like-google-analytics) above. Every other tip here follows the same idea: reuse Google's naming, don't invent your own.
* **Use `snake_case`**: `add_to_cart`, `sign_up`, `purchase`. This matches Google Analytics 4 and Google Tag Manager conventions, so the same event names work end-to-end if you also send events to GTM.
* **Prefer the standard GA4 names** where one fits: `page_view`, `view_item`, `add_to_cart`, `begin_checkout`, `purchase`, `sign_up`, `search`, `generate_lead`. Reusing GA4's recommended event names means your data lines up with any GTM/GA4 tagging you already have.
* **Keep names short and consistent**: reuse the same name everywhere that action happens. One `purchase` event is far more useful than `purchase`, `Purchase`, and `buy_now`.
* **Name the action, not the place**: `cta_click` is more reusable than `homepage_blue_button_click` — the place belongs in scope, not the name.

### Scope values [#scope-values]

* **Name the place, not the action**: `homepage_hero_login`, `checkout`, `product_page`. The action is already the event name; scope answers "where" or "which one."
* **Pick a stable identifier, not display text**: a button's scope shouldn't change when someone edits its copy. Use a slug tied to the button/block/page itself, the same way `page_view`'s scope is a URL path rather than a page title.
* **Reuse scopes across events when they refer to the same place**: a `page_view` and a `form_submit` on the same signup page can both use `signup` as their scope, making it easy to see the whole funnel for that page at a glance.

Once your events are flowing and attached to a test, see [What to A/B test](/docs/guides/what-to-test) for ideas on what to run.
