/assessing-heatmaps
Assesses what a page's heatmap is telling you and recommends concrete changes. Pulls click / rageclick / scroll-depth data for a URL, names the hot elements by cross-referencing autocapture events on the same page, and can create a saved heatmap the user opens in PostHog, then
Claude Code
$ npx -y skills add PostHog/ai-plugin --skill assessing-heatmaps --agent claude-code
Codex
$ npx skills add PostHog/ai-plugin --skill assessing-heatmaps -a codex -y
Cursor
$ npx skills add PostHog/ai-plugin --skill assessing-heatmaps -a cursor -y
Gemini CLI
$ npx skills add PostHog/ai-plugin --skill assessing-heatmaps -a gemini-cli -y
GitHub Copilot
$ npx skills add PostHog/ai-plugin --skill assessing-heatmaps -a github-copilot -y
OpenCode
$ npx skills add PostHog/ai-plugin --skill assessing-heatmaps -a opencode -y
Windsurf
$ npx skills add PostHog/ai-plugin --skill assessing-heatmaps -a windsurf -y
SKILL.md
assessing-heatmaps.SKILL.mdname: assessing-heatmaps
description: "Assesses what a page's heatmap is telling you and recommends concrete changes. Pulls click / rageclick / scroll-depth data for a URL, names the hot elements by cross-referencing autocapture events on the same page, and can create a saved heatmap the user opens in PostHog, then summarizes the behavior and proposes improvements.\nTRIGGER when: user asks what a heatmap shows, why people aren't clicking something, where users rage-click, how far they scroll, what to change on a page based on heatmap/click data, or to 'analyze/assess/review the heatmap' for a URL.\nDO NOT TRIGGER when: the user only wants to create a saved heatmap screenshot with no analysis (use heatmaps-saved-create directly), or is asking about session replay in general (use investigating-replay)."
Assessing heatmaps
A heatmap answers "where do people interact with this page?" — clicks, rage clicks, mouse movement, and how far down they scroll. The data is pure geometry: `pointer_relative_x` (0..1 across the viewport), `pointer_y` (absolute pixels down the page), and a count per spot. **It does not know what was clicked.** Turning "lots of clicks at (0.5, 220)" into "lots of clicks on the Pricing nav link" is the whole job, and it comes from cross-referencing autocapture on the same URL.
Core principle: coordinates + meaning
You can't see the page — there is no screenshot in your context. A good assessment fuses two sources and leans on autocapture to supply the layout/identity you can't see:
1. **Heatmap data** — where interactions land and how far people scroll (`heatmaps-list`). 2. **Autocapture** — what element sits under the hot spots, by element text / selector on the same page. This is what turns coordinates into meaning; without it you only have dots.
When the user wants to _see_ the heatmap, create a saved heatmap (Step 4) — that renders the page with the data overlaid for them to open in PostHog. You reason from the data; they look at the picture.
The flow
Step 1: Pin the page and window
You need an exact `url_exact` (one page) or a `url_pattern` (regex, to aggregate across query strings). Confirm the URL with the user if ambiguous. Default to the last 7 days; widen to 30 if volume is low. Heatmap data is retained for 90 days.
Step 2: Pull the data
Call `heatmaps-list` once per signal you care about (or query the `heatmaps` table directly via SQL — see the querying-posthog-data skill, `models-heatmaps`):
- `type: "click"` — the primary "what draws attention" map.
- `type: "rageclick"` — repeated frustrated clicks. **The single strongest "something is broken or
misleading" signal.** Any meaningful rageclick cluster deserves a callout.
- `type: "scrolldepth"` — how far people get. Use it to find the fold and spot CTAs that sit below where most
people ever scroll.
Use `aggregation: "unique_visitors"` when you care about how many people (not how many clicks); `total_count` exaggerates a few heavy clickers.
Click results come back **hottest-first** and are capped at `limit` (default 500). A busy page can have thousands of distinct coordinates, so the default page plus the `fold` summary is almost always enough — the hottest points are what analysis turns on. Don't ask for everything: raise `limit` or page with `offset` only when you specifically need more, and check `has_more` to know the list was truncated. `scrolldepth` ignores `limit` and always returns every bucket.
Step 2b: Above the fold — read the `fold` summary
For the click types, `heatmaps-list` returns a `fold` object alongside `results`:
- `pct_below_fold` — share of non-fixed interactions that landed **below the user's initial viewport** (they
had to scroll to reach them). This is one of the highest-value findings: content people actively click that sits below the fold is a prime candidate to move up.
- `below_fold_count` / `total_count` — the raw counts behind the percentage (fixed-position elements are
excluded, since they're always on screen).
- `median_viewport_height` — the typical fold line in CSS pixels, to recommend against.
Report it concretely, e.g. "the fold is ~600px for most visitors, yet 35% of clicks land below it, so users scroll before interacting — that content is a candidate for the first screen." **Segment by device** with `viewport_width_min`/`viewport_width_max` (desktop and mobile have very different folds) and read `fold` per band rather than blending them.
Need a distribution rather than a single percentage (e.g. clicks bucketed by how far below the fold)? Drop to SQL on the raw `heatmaps` table, which has `y` and `viewport_height` in the same scaled units — see the querying-posthog-data skill, `models-heatmaps`.
Step 3: Name the hot elements (autocapture overlap)
For each notable cluster, find what's actually there. Query autocapture on the same URL — either via the `exploring-autocapture-events` skill or directly:
SELECT properties.$el_text AS text, count() AS clicks
FROM events
WHERE event = '$autocapture'
AND properties.$current_url = 'https://example.com/pricing'
AND timestamp >= now() - INTERVAL 7 DAY
GROUP BY text
ORDER BY clicks DESC
LIMIT 25
`elements_chain` gives the selector/DOM path when you need to disambiguate two elements with the same text. Match autocapture's top elements to the heatmap's hot coordinates: clicks concentrated on something that is **not** a link or button (plain text, an image, a disabled control) is a classic "users expect this to be clickable" finding.
Step 4: Give the user a heatmap to look at (optional)
You can't see the page, but the user can. When a visual would help them follow your findings, create a saved heatmap so they can open the rendered page with the data overlaid in PostHog:
1. `heatmaps-saved-create` with the page `url` (type defaults to `screenshot`). This enqueues a headless render — it is asynchronous. Pass `widths` matching the viewport band you ana
Read more
name: assessing-heatmaps description: "Assesses what a page's heatmap is telling you and recommends concrete changes. Pulls click / rageclick / scroll-depth data for a URL, names the hot elements by cross-referencing autocapture events on the same page, and can create a saved heatmap the user opens in PostHog, then summarizes the behavior and proposes improvements.\nTRIGGER when: user asks what a heatmap shows, why people aren't clicking something, where users rage-click, how far they scroll, what to change on a page based on heatmap/click data, or to 'analyze/assess/review the heatmap' for a URL.\nDO NOT TRIGGER when: the user only wants to create a saved heatmap screenshot with no analysis (use heatmaps-saved-create directly), or is asking about session replay in general (use investigating-replay)."
Assessing heatmaps
A heatmap answers "where do people interact with this page?" — clicks, rage clicks, mouse movement, and how far down they scroll. The data is pure geometry: `pointer_relative_x` (0..1 across the viewport), `pointer_y` (absolute pixels down the page), and a count per spot. **It does not know what was clicked.** Turning "lots of clicks at (0.5, 220)" into "lots of clicks on the Pricing nav link" is the whole job, and it comes from cross-referencing autocapture on the same URL.
Core principle: coordinates + meaning
You can't see the page — there is no screenshot in your context. A good assessment fuses two sources and leans on autocapture to supply the layout/identity you can't see:
1. **Heatmap data** — where interactions land and how far people scroll (`heatmaps-list`). 2. **Autocapture** — what element sits under the hot spots, by element text / selector on the same page. This is what turns coordinates into meaning; without it you only have dots.
When the user wants to _see_ the heatmap, create a saved heatmap (Step 4) — that renders the page with the data overlaid for them to open in PostHog. You reason from the data; they look at the picture.
The flow
Step 1: Pin the page and window
You need an exact `url_exact` (one page) or a `url_pattern` (regex, to aggregate across query strings). Confirm the URL with the user if ambiguous. Default to the last 7 days; widen to 30 if volume is low. Heatmap data is retained for 90 days.
Step 2: Pull the data
Call `heatmaps-list` once per signal you care about (or query the `heatmaps` table directly via SQL — see the querying-posthog-data skill, `models-heatmaps`):
- `type: "click"` — the primary "what draws attention" map.
- `type: "rageclick"` — repeated frustrated clicks. **The single strongest "something is broken or
misleading" signal.** Any meaningful rageclick cluster deserves a callout.
- `type: "scrolldepth"` — how far people get. Use it to find the fold and spot CTAs that sit below where most
people ever scroll.
Use `aggregation: "unique_visitors"` when you care about how many people (not how many clicks); `total_count` exaggerates a few heavy clickers.
Click results come back **hottest-first** and are capped at `limit` (default 500). A busy page can have thousands of distinct coordinates, so the default page plus the `fold` summary is almost always enough — the hottest points are what analysis turns on. Don't ask for everything: raise `limit` or page with `offset` only when you specifically need more, and check `has_more` to know the list was truncated. `scrolldepth` ignores `limit` and always returns every bucket.
Step 2b: Above the fold — read the `fold` summary
For the click types, `heatmaps-list` returns a `fold` object alongside `results`:
- `pct_below_fold` — share of non-fixed interactions that landed **below the user's initial viewport** (they
had to scroll to reach them). This is one of the highest-value findings: content people actively click that sits below the fold is a prime candidate to move up.
- `below_fold_count` / `total_count` — the raw counts behind the percentage (fixed-position elements are
excluded, since they're always on screen).
- `median_viewport_height` — the typical fold line in CSS pixels, to recommend against.
Report it concretely, e.g. "the fold is ~600px for most visitors, yet 35% of clicks land below it, so users scroll before interacting — that content is a candidate for the first screen." **Segment by device** with `viewport_width_min`/`viewport_width_max` (desktop and mobile have very different folds) and read `fold` per band rather than blending them.
Need a distribution rather than a single percentage (e.g. clicks bucketed by how far below the fold)? Drop to SQL on the raw `heatmaps` table, which has `y` and `viewport_height` in the same scaled units — see the querying-posthog-data skill, `models-heatmaps`.
Step 3: Name the hot elements (autocapture overlap)
For each notable cluster, find what's actually there. Query autocapture on the same URL — either via the `exploring-autocapture-events` skill or directly:
SELECT properties.$el_text AS text, count() AS clicks FROM events WHERE event = '$autocapture' AND properties.$current_url = 'https://example.com/pricing' AND timestamp >= now() - INTERVAL 7 DAY GROUP BY text ORDER BY clicks DESC LIMIT 25
`elements_chain` gives the selector/DOM path when you need to disambiguate two elements with the same text. Match autocapture's top elements to the heatmap's hot coordinates: clicks concentrated on something that is **not** a link or button (plain text, an image, a disabled control) is a classic "users expect this to be clickable" finding.
Step 4: Give the user a heatmap to look at (optional)
You can't see the page, but the user can. When a visual would help them follow your findings, create a saved heatmap so they can open the rendered page with the data overlaid in PostHog:
1. `heatmaps-saved-create` with the page `url` (type defaults to `screenshot`). This enqueues a headless render — it is asynchronous. Pass `widths` matching the viewport band you ana
Official PostHog plugin for AI clients. Access PostHog products directly from your AI coding tool.
Other skills on ai-plugin.
More skills from other plugins.
brainstorming
You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…
ask-matt
Ask which skill or flow fits your situation. A router over the skills in this repo.
Questions about assessing-heatmaps.
- Which coding agents can use assessing-heatmaps?
- assessing-heatmaps installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.

