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

# ReportChartElement

> An advertising account’s performance charted over a window you choose, with a picker for which metric to plot — spend, impressions, clicks, or any conversion the account records. Read-only, and the chart only: headline figures belong to whatever surface composes this.

<Info>**Upcoming** — generated from the latest merged element source; documents unreleased development. Use the channel picker at the top of the sidebar for the docs of a published release.</Info>

*In development — not yet part of a stable release.*

Belongs to the [`Ads`](/elements/upcoming/ads/overview) group. Render `<ReportChartElement />` inside it (React), or call `ads.create('report-chart', { … })` on the handle (vanilla) — consumer props and `on<Event>` callbacks both go in the create options / JSX props.

## Preview

A live, interactive demo of this element with sample data:

<div data-whop-demo-shell style={{ position: "relative", minHeight: "320px", transition: "min-height 200ms ease" }}>
  <div data-whop-demo-skeleton style={{ position: "absolute", inset: "0", borderRadius: "12px", background: "rgba(140, 140, 140, 0.12)", pointerEvents: "none", transition: "opacity 200ms ease" }} />

  <div data-whop-demo-native="element:ads/report-chart" data-whop-elements-version="" style={{ position: "relative" }} />
</div>

## Usage

<div data-whop-usage="ads/report-chart">
  <CodeGroup>
    ```tsx React theme={null}
    import { WhopElements, Ads, ReportChartElement } from "@whop/elements-react";
    import { loadWhop } from "@whop/elements";

    function Example() {
      return (
        <WhopElements elements={loadWhop()}>
          <Ads /* options */>
            <ReportChartElement onMetricChanged={(payload) => console.log("metricChanged", payload)} onTimezoneChanged={(payload) => console.log("timezoneChanged", payload)} onPeriodChanged={(payload) => console.log("periodChanged", payload)} />
          </Ads>
        </WhopElements>
      );
    }
    ```

    ```html Vanilla theme={null}
    <script src="https://js.whop.cloud/elements/amber/elements.js" data-whop-elements></script>
    <script type="module">
      const ads = window.WhopElements().ads.create({ /* options */ });
      ads.create('report-chart', {
        onMetricChanged: (payload) => console.log("metricChanged", payload),
        onTimezoneChanged: (payload) => console.log("timezoneChanged", payload),
        onPeriodChanged: (payload) => console.log("periodChanged", payload)
      }).mount('#ads-report-chart');
    </script>
    ```
  </CodeGroup>
</div>

## Props

<ResponseField name="accessToken" type="string">
  A scoped token for the reads — it needs `ad_campaign:read` and `stats:read`. Mint it on your server with `POST /v1/access_tokens` and set a fresh one before it expires. Omitted, the reads carry the viewer's own session, which only answers same-origin. To have the chart pick up the account's own reporting timezone and currency it additionally needs `ad_campaign:create`, which is what the preferences endpoint asks for; without it the chart reports in the viewer's zone unless you set `timezone`/`currency` on the handle.
</ResponseField>

## Events

Pass a callback in the create options (or React prop) to receive these.

### `onMetricChanged`

The viewer picked a different result to plot. `metric` is what is being plotted; `event` names the conversion it refers to, and is present only for `conversions`, `conversion_value` and `cost_per_conversion` — the account decides what those are called, so it is a string rather than a member. Mirror it into your own URL if you want the choice to survive a reload; the element keeps it for the life of the mount either way.

**Signature:** `((payload: { metric: "funnel" | "spend" | "impressions" | "clicks" | "cost_per_1k_impressions" | "cost_per_click" | "conversions" | "conversion_value" | "cost_per_conversion"; event?: string | undefined; }) => void)`

### `onTimezoneChanged`

The viewer chose a different zone from the picker. The element holds it for the life of the mount only — persist it and pass it back as `timezone` if you want the choice to outlive a reload, which is what the Whop dashboard does with its own copy.

**Signature:** `((payload: { timezone: string; }) => void)`

### `onPeriodChanged`

The viewer moved the reporting window: the period they picked AND the window it resolves to, as ISO 8601 instants. The dates come resolved because a period name alone would leave you redoing the day-boundary and account-timezone arithmetic to act on it; the period comes too because only it survives a reload as a ROLLING window — stored dates would pin it. Every surface under the same handle has already followed the move.

**Signature:** `((payload: { period: "today" | "yesterday" | "last_7_days" | "last_14_days" | "last_30_days" | "last_90_days" | "all_time" | "custom"; from: string; to: string; }) => void)`

### `onLoaderStart`

Fired the moment the element's own loading skeleton has painted inside its frame — the earliest point a consumer-managed loading state can hand off without ever exposing a blank. Always precedes `onReady`; most consumers only need `onReady`.

**Signature:** `(() => void)`

### `onReady`

Fired once the element has booted and painted its first complete frame.

**Signature:** `(() => void)`

### `onError`

Fired when the element fails to load or crashes; the element shows its own error fallback. `message` is human-readable; framework refusals also carry `code` (e.g. `HOST_SOURCE_FAILED`, with `sourceKey` naming the failed hostState key) so hosts can switch on codes, never message text.

**Signature:** `((e: { message: string; code?: string | undefined; sourceKey?: string | undefined; }) => void)`

## Methods

Call these on the element handle — the return of `create` (vanilla) or the component `ref` (React).

### `mount`

Place the element on the page: appends its container to `target` (a CSS selector or an element) and starts loading. Nothing renders until this is called. React consumers never call it — the component mounts itself.

**Signature:** `(target: string | HTMLElement) => void`

### `destroy`

Remove the element from the page and release its frame and subscriptions. Safe to call more than once. React consumers never call it — unmounting the component does it.

**Signature:** `() => void`

### `update`

Change this element's consumer props after mount — it re-renders with the merged props. React consumers never call it — updating the JSX props does the same.

**Signature:** `(options: Partial<ReportChartElementProps>) => void`

## Styling

This element exposes no targetable class names. Use `appearance` (theme, accent color, variables) to restyle it.
