New to Claude Skills? Learn how to install them →

posthog on GitHub

Querying Canvas Data

Free

Integrate PostHog data seamlessly into your canvas.

by posthog37.6k stars on posthog/posthog
1 views
Updated Aug 11, 2026
Get this skill

Free · Opens the source repo

What Querying Canvas Data does

The Querying Canvas Data skill enables developers to effectively integrate PostHog analytics into canvas applications. By utilizing the host-injected ph SDK, this skill allows for the retrieval and display of metrics, charts, and tables directly from PostHog. It ensures that all interactions with PostHog data are secure and compliant with sandbox restrictions, as credentials remain with the host. This skill is particularly useful for developers looking to visualize analytics data within their applications without needing to manage authentication or data fetching manually.

The skill employs a structured data hierarchy for querying metrics, emphasizing the importance of using saved insights whenever possible. Developers can create insights using various query types such as TrendsQuery and FunnelsQuery, ensuring that the metrics displayed are reliable and consistent with what users see in the PostHog UI. If a saved insight does not fit the use case, developers can resort to ad-hoc queries or inline HogQL, although the latter should be used as a last resort due to potential inaccuracies in metrics.

Additionally, the skill provides clear guidelines on how to handle result shapes from queries, ensuring that developers can accurately render data in their applications. It emphasizes the importance of managing loading states and error handling to enhance user experience. By adhering to these practices, developers can avoid common pitfalls like displaying incorrect data or failing to communicate errors effectively.

Overall, Querying Canvas Data is designed for developers who need to integrate PostHog analytics into their canvas applications efficiently. It streamlines the process of querying and displaying data while ensuring best practices are followed for optimal performance and accuracy.

When to use it

Use this skill when you need to display PostHog metrics, charts, or tables in a canvas application or capture analytics events.

When not to use it

This skill is not suitable for applications that do not use PostHog for analytics or require raw SQL queries without the structured insights.

What you can build with it

Displaying Metrics on a Dashboard

Integrate PostHog metrics into a canvas-based dashboard, using saved insights to ensure accuracy.

Capturing User Interactions

Capture analytics events from user interactions within the canvas using the `ph.capture` method.

Handling Date Ranges for Insights

Dynamically adjust the date range of insights displayed in the canvas based on user-selected date windows.

How to install Querying Canvas Data

View source

1. Install with the skills CLI

npx skills add posthog/posthog/querying-canvas-data --agent claude-code

2. Or install it manually

Download the skill folder and drop it into ~/.claude/skills/ for all projects, or .claude/skills/ to scope it to one repo. Restart Claude Code so it picks up the new skill.

Anthropic's agentic coding CLI, and the reference implementation of Agent Skills. Drop a skill folder into ~/.claude/skills and Claude Code loads it automatically whenever a task matches the skill's description. Claude Code docs

Inside SKILL.md

Written by posthog

Querying canvas data

The global ph object (injected by the host — never imported, never initialized) is the only way a canvas talks to PostHog. Credentials stay in the host; fetch(), posthog-js, and hand-rolled clients fail in the sandbox.

Data hierarchy — back every metric with a saved insight

  1. Preferred — save an insight, load it by reference. Use the PostHog MCP insight tools to create/save an insight that computes the metric with an insight query type (TrendsQuery, FunnelsQuery, RetentionQuery, PathsQuery, or the web-analytics kinds WebOverviewQuery / WebStatsTableQuery — not raw SQL). Confirm its numbers, note the short_id, and render it with await ph.loadInsight(shortId, { dateRange }). These are proven queries — numbers match the PostHog UI exactly (sessionization, unique users, breakdowns, bounce rate). Never fabricate a query or guess event/property names; discover and save them via MCP first.
  2. Secondary — an ad-hoc typed node: ph.query({ kind: "TrendsQuery", series: [...], dateRange: {...} }) when saving an insight genuinely doesn't fit.
  3. Last resort — inline HogQL: ph.query("SELECT …"), only when no insight kind can express the metric; you then own the SQL and its date window.

For web-analytics boards specifically, use the web-analytics query kinds — raw HogQL subtly gets bounce rate, sessionization, channel attribution, and unique-visitor counts wrong.

Whatever tier you use, declare it in the project's capabilities before publishing: every ph.loadInsight short id in capabilities.posthog.insights, every ph.capture event name in captureEvents, and inlineQueries: true for any ph.query use. The host rejects undeclared calls at runtime, and validation fails on undeclared literals.

Result shapes — read them correctly or every value renders 0

  • Trends-style results (insight query types, via ph.loadInsight or a typed node): results is an array of series objects, not rows. Each series has data: number[] (per interval), days: string[] (ISO), labels: string[], count (sum), aggregated_value (single-value total), label, and optional compare_label: "current" | "previous". A KPI total is results[0].count (or .aggregated_value); a line chart plots results[0].data over results[0].days. With a compare period, find the prior series by compare_label === "previous" — never by index. columns is empty here.
  • SQL results: { columns: string[], results: rows[][] } — each row an array of cell values in columns order.

Load data in useEffect with useState, show a loading state, and aggregate in the query; never fetch raw event dumps. Treat a rejected query and an empty result as different states: .catch must set an error state that renders visibly (message + retry), never fall through to zeros, an empty chart, or a "no data" message — a swallowed error makes real breakage (a missing table, an auth failure) look like missing data. Reserve the empty state for a query that succeeded with no rows.

Date windows

  • Pass the canvas's date-picker window straight into dateRange: ph.loadInsight(shortId, { dateRange: { date_from: win.start.toISOString(), date_to: win.end.toISOString() } }) — the saved insight re-scopes to the window with no time SQL. Typed nodes take the same dateRange. Re-run every query when the window changes.
  • A saved SQL insight may ignore dateRange (its window lives inside the SQL) — a reason to prefer insight query types.
  • Inline HogQL escape hatch only: never bake now() or a hardcoded INTERVAL. Compute unix bounds (Math.floor(win.start.getTime() / 1000)) and write half-open timestamp >= toDateTime(fromUnix) AND timestamp < toDateTime(toUnix). Prior period = the equal-length window immediately before; bucket with toStartOfDay/toStartOfHour.

Side effects

  • ph.capture(event, properties?, distinctId?) — analytics events for interactions (fire-and-forget). Session replay, $session_id, and person attribution are handled by the host automatically; never roll your own capture.
  • ph.openExternal(url) — opens https://posthog.com / *.posthog.com URLs only, and only from a user interaction (opens outside focus are ignored). Don't link elsewhere.
  • ph.navigate.toTask(id) / .toNewTask() / .toCanvas(id) / .toNewCanvas() — in-app navigation within the canvas's own channel.

Frequently asked questions about Querying Canvas Data

Similar skills