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.
Reporting sub-controller in the Ads group. Render <TableElement /> inside <Reporting> (React), or mint the sub and mount off it (vanilla): ads.create('reporting', { … }).create('table', { … }) — 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:Usage
Props
string
A scoped token. Reading needs
ad_campaign:read, ad_group:read, ad:read and stats:read; the row actions additionally need the matching :update and :delete scopes, and duplicating needs :create. Mint it on your server with POST /v1/access_tokens and set a fresh one before it expires. Omitted, the calls carry the viewer’s own session, which only answers same-origin. To have the table 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 figures are reported in the viewer’s zone unless you set timezone/currency on the handle.boolean
What happens when a viewer asks to edit — from a row menu, a draft row, or Edit ad in the details overlay. On by default: the campaign builder opens over the page, on the campaign and step they asked for, so editing works with nothing wired up. Turn it off to handle campaign editing yourself — nothing opens, and the request comes back on
editRequested. Defaults to true."age" | "gender" | "age_gender" | "placement" | "publisher_platform" | "device_platform" | "impression_device" | "country" | "region" | "hour" | null
How rows are broken down — by country, platform, placement — or
null for whole rows. Defaults to null.string[] | null
The columns on show, by name — standard ones like
impressions and roas, and any pixel events the viewer added. Unset, the table opens on its own defaults. Comes back whole on columnsChanged; store the list and pass it here to have the layout stick. Defaults to null.CustomMetricDefinition[]
Columns computed from the figures already in the table — a formula over them, named and formatted by you. The table can edit them, and every change comes back on
customMetricsChanged as the whole new list; persist it and pass it here to have it stick. Defaults to [].string | null
What the All tab is drilled into, as a source path:
null for the roots, ext:* for a platform list, ext:<platform>:* for that platform’s campaigns. Comes back on viewChanged. Defaults to null.string
What the Ads tab’s search box is filtered to. Defaults to
"".boolean
Whether paused campaigns are listed. Defaults to
true.Direct mode only
These props exist only on the in-process (direct-mode) component — the hosted embed never carries them, and the element renders without them.direct mode only
Direct mode only. Leave the viewer’s columns and computed metrics to the app around the table — its own store, its own defaults — instead of taking them from
columns and customMetrics. An embed cannot set this and should not want to: localStorage inside a frame belongs to the bundle’s origin, so a layout chosen there is invisible to everything else you run.Direct mode only. Hand the table your router and its links become real navigations — a
Link for the row menus, a navigate that honours cmd-click. Left unset, and in every embed, links raise editRequested instead: an element frame has no router, and a plain <a> would move the frame rather than the page around it.Events
Pass a callback in the create options (or React prop) to receive these.onTabChanged
The open tab changed. Mirror it into your own URL if you want it to survive a reload.
Signature: ((payload: { tab: "ads" | "all" | "campaigns" | "ad-groups"; }) => void)
onFilterChanged
The viewer narrowed the view — by ticking rows, or by clicking through a campaign to its ad groups. Every surface under the same handle has already followed it.
Signature: ((payload: { campaignIds: string[]; adGroupIds: string[]; adIds: string[]; }) => void)
onColumnsChanged
The viewer showed or hid a column. Carries every column now on show, standard and pixel-event alike — store the list and pass it straight back as columns, or the layout resets on the next mount.
Signature: ((payload: { columns: string[]; }) => void)
onCustomMetricsChanged
The viewer added, edited or removed a computed column. Carries the WHOLE list either way — store it as given and pass it back as customMetrics, or they are gone on the next mount.
Signature: ((payload: { customMetrics: CustomMetricDefinition[]; }) => void)
onViewChanged
The viewer moved one of the table’s own controls — the All tab’s drill, the breakdown, the search box, the paused toggle. Carries all four whichever moved; store them and pass them back as props, or they reset on the next mount.
Signature: ((payload: { sourcesDrill: string | null; breakdown: "age" | "gender" | "age_gender" | "placement" | "publisher_platform" | "device_platform" | "impression_device" | "country" | "region" | "hour" | null; query: string; showPausedCampaigns: boolean; }) => void)
onSourcesChanged
What the All tab is now looking at, as source paths ready to hand to a chart: null off the All tab (Whop advertising alone), [] for every source, otherwise the ticked rows or the drilled-into group. Every surface under the same handle has already followed it.
Signature: ((payload: { sources: string[] | null; }) => void)
onAttributionModelChanged
The viewer changed which touch gets the credit, from the picker on the All tab. Every surface under the same handle has already re-credited; persist it and pass it back as attributionModel, or it reverts to last touch on the next mount.
Signature: ((payload: { attributionModel: "last_touch" | "first_touch"; }) => 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 ofcreate (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<TableElementProps>) => void
Styling
This element exposes no targetable class names. Useappearance (theme, accent color, variables) to restyle it.
