Guides exploration of $autocapture events captured by posthog-js to understand user interactions, find CSS selectors (especially data-attr attributes), evaluate selector uniqueness, query matching clicks ad-hoc, and create actions. Use when the user asks about autocapture data, wants to find what users are clicking, needs to build actions from click events, or asks about elements_chain. Only applies to projects using posthog-js autocapture.
if users opt in then posthog-js automatically captures clicks, form submissions, and page changes as $autocapture events.
Each event records the clicked DOM element and its ancestors in the elements_chain column.
$autocapture is intentionally excluded from the posthog:read-data-schema taxonomy
because it is only useful with autocapture-specific filters (selector, tag, text, href).
This skill fills that gap.
The events table provides fast access to common element fields without parsing the full chain string.
| Column | Type | Description |
|---|---|---|
elements_chain | String | Full semicolon-separated element chain (see format reference) |
elements_chain_href | String | Last href value from the chain |
elements_chain_texts |
| Array(String) |
| All text values from elements |
elements_chain_ids | Array(String) | All id attribute values |
elements_chain_elements | Array(String) | Useful tag names: a, button, input, select, textarea, label |
Use materialized columns for exploration queries whenever possible — they avoid regex parsing.
Run a count query before doing anything else. If the count is zero, autocapture may be disabled. There are two ways this happens:
autocapture_opt_out in PostHog project settingsinit() call can pass autocapture: falseTell the user if no data is found so they can check both settings.
SELECT count() as cnt
FROM events
WHERE event = '$autocapture'
AND timestamp > now() - INTERVAL 7 DAY
Start broad using the materialized columns. The goal is to understand what users are clicking before narrowing down.
Useful explorations:
elements_chain_elements)elements_chain_texts)elements_chain_href)elements_chain values for a specific page (filtered by properties.$current_url)See example queries for all patterns.
Once the user identifies an interaction they care about, find a CSS selector that identifies it.
Priority order for selector attributes (best first):
data-attr or other data-* attributes — highest specificity, stable across deploys, developer-intended anchors.
Search with match(elements_chain, 'data-attr=') or extractAll.attr_id) — also highly stable, queryable via elements_chain_ids.When a data-attr value is found, construct a selector like [data-attr="value"] or button[data-attr="value"].
A selector is only useful if it matches the intended interaction and not unrelated events.
Run a uniqueness check using elements_chain =~ with the regex pattern for the selector.
Then sample matching events to inspect what the selector actually captures.
Compare the count against total autocapture volume to understand selectivity.
A good selector matches a single logical interaction. If it matches too many distinct elements, refine it in the next step.
If the selector alone is not unique enough, layer on additional filters:
elements_chain_textsproperties.$current_urlelements_chain_hrefRe-run the uniqueness check after each refinement. Only include filters that are needed — fewer filters means more resilience to minor DOM changes.
The discovered selector can be used directly in HogQL without creating an action.
Trends — count matching clicks over time:
SELECT
toStartOfDay(timestamp) as day,
count() as clicks
FROM events
WHERE event = '$autocapture'
AND timestamp > now() - INTERVAL 14 DAY
AND elements_chain =~ '(^|;)button.*?data-attr="checkout"'
GROUP BY day
ORDER BY day
Funnel — pageview to click conversion:
SELECT
person_id,
first_pageview,
first_click_after
FROM (
SELECT
p.person_id,
p.pageview_time as first_pageview,
min(c.click_time) as first_click_after
FROM (
SELECT person_id, min(timestamp) as pageview_time
FROM events
WHERE event = '$pageview'
AND timestamp > now() - INTERVAL 14 DAY
AND properties.$current_url ILIKE '%/pricing%'
GROUP BY person_id
) p
INNER JOIN (
SELECT person_id, timestamp as click_time
FROM events
WHERE event = '$autocapture'
AND timestamp > now() - INTERVAL 14 DAY
AND elements_chain =~ '(^|;)button.*?data-attr="signup"'
) c ON p.person_id = c.person_id AND c.click_time > p.pageview_time
GROUP BY p.person_id, p.pageview_time
)
For recurring analysis, prefer creating an action (next step) or using posthog:query-trends / posthog:query-funnel with the action.
Actions are the durable version of ad-hoc selector queries.
Once the criteria uniquely identify the interaction, create an action using posthog:action-create.
Construct the step with only the filters needed for uniqueness:
{
"name": "Clicked checkout button",
"steps": [
{
"event": "$autocapture",
"selector": "button[data-attr='checkout']",
"text": "Complete Purchase",
"text_matching": "exact",
"url": "/checkout",
"url_matching": "contains"
}
]
}
Available step fields for $autocapture:
selector — CSS selector (e.g. button[data-attr='checkout'])tag_name — HTML tag name (e.g. button, a, input)text / text_matching — element text (exact, contains, or regex)href / href_matching — link href (exact, contains, or regex)url / url_matching — page URL (exact, contains, or regex)After creation, verify with matchesAction():
SELECT count() as matching_events
FROM events
WHERE matchesAction('Clicked checkout button')
AND timestamp > now() - INTERVAL 7 DAY
$autocapture is high volumeLIMIT generously when sampling elements_chain — the strings can be longelements_chain =~ operator matches CSS selectors as regex internally;
prefer materialized columns when possible for performance$autocapture events also carry standard properties like $current_url, $browser, $os