/building-canvases
Create or edit a PostHog freeform canvas — a sandboxed browser application (data board, document, form, small tool, graphics experiment) stored in PostHog and rendered by the desktop/web app. Use when a task asks to build, generate, update, or fix a standalone canvas app, or
Claude Code
$ npx -y skills add PostHog/ai-plugin --skill building-canvases --agent claude-code
Codex
$ npx skills add PostHog/ai-plugin --skill building-canvases -a codex -y
Cursor
$ npx skills add PostHog/ai-plugin --skill building-canvases -a cursor -y
Gemini CLI
$ npx skills add PostHog/ai-plugin --skill building-canvases -a gemini-cli -y
GitHub Copilot
$ npx skills add PostHog/ai-plugin --skill building-canvases -a github-copilot -y
OpenCode
$ npx skills add PostHog/ai-plugin --skill building-canvases -a opencode -y
Windsurf
$ npx skills add PostHog/ai-plugin --skill building-canvases -a windsurf -y
SKILL.md
building-canvases.SKILL.mdname: building-canvases
description: >
Create or edit a PostHog freeform canvas — a sandboxed browser application (data board, document,
form, small tool, graphics experiment) stored in PostHog and rendered by the desktop/web app. Use
when a task asks to build, generate, update, or fix a standalone canvas app, or when a freeform
canvas id is given as the publish target. For grid/home canvases, widget placements, or reusable
components, use composing-grid-canvases instead. Covers resolving or creating the target canvas,
choosing an implementation approach (React + Quill vs plain HTML/browser APIs), the read → edit →
validate → publish → build loop, and which companion canvas skills to load for the details.
Building canvases
A canvas is a client-side browser application that runs in a sandboxed iframe inside PostHog. Its source lives in PostHog — not in a repository — and you read and write it through the `canvas-*` tools. Never write a canvas to a local file; publishing through the tool is what saves it.
Canvas work can start from any ordinary task. A dedicated canvas mode or pre-created canvas is not required. When the user asks for a board, document, form, visualization, or small app that should live in PostHog, treat that as a canvas request and follow this skill.
This skill owns `freeform` canvases (standalone apps). Two other canvas kinds exist: `grid` canvases (widget grids, including the user's home canvas) and `component` canvases (reusable widgets grids place). When the target is a grid or home canvas, a placement, or a reusable widget/component, load `composing-grid-canvases` instead — it owns the store search → configure → fork → build ladder and the layout patch loop. Authoring a component's source still uses the implementation companions below.
Resolve the target canvas
- If the task names a canvas id (canvas-initiated tasks do), that is the target. Do not create another.
- Otherwise the target channel is the one the task was created in — named in the task's context
(the `channel_context` block or the generation instructions). List that channel's canvases with `canvas-list` (scope with `channel`). If one is clearly what the request refers to — an earlier iteration of the same board or tool — build on it instead of creating a near-duplicate, and say so in your reply so the user knows where the result landed.
- Only when nothing existing fits, create one with `canvas-create` in that same channel, named
with a short descriptive title drawn from the request — never "Untitled canvas".
- Never survey channels to choose a target yourself: use `channel-list` only to resolve a channel
the USER named to its id. Its listing puts the personal #me channel first, and #me is never a default — a canvas filed there is invisible to everyone else. If the task names neither a canvas nor a channel, ask which channel to use instead of guessing.
Load the companion skills for the implementation
This skill owns canvas selection and the authoring lifecycle. The companion skills hold the implementation contracts. Load every companion that applies before writing source:
- **`building-react-quill-canvases`** for dashboards, data boards, forms, tools, application-like
state, or anything that should look native to PostHog. It owns allowed imports, Quill composition, theming, charts, loading and error states, and the date picker.
- **`building-html-canvases`** for documents, articles, focused experiments, generative graphics,
`<canvas>`, or WebGL work where application components add no useful structure. It owns semantic markup, direct browser APIs, animation cleanup, and non-Quill theming.
- **`querying-canvas-data`** whenever the canvas reads PostHog data, captures events, or navigates.
It owns the `ph` SDK, saved-insight preference, result shapes, variables, date ranges, progressive per-query loading, and declared data capabilities. Load it alongside either implementation skill when data is involved.
- **`validating-and-publishing-canvases`** for every canvas. It owns project shape, capability
declarations, validation diagnostics, guarded publishes, drafts, builds, and conflict recovery.
Mix implementation approaches when appropriate: React can own application chrome while browser graphics code owns a canvas element, or a mostly static page can mount one interactive island.
This is a judgment call, not a persisted mode — ask the user only when the choice changes a user-visible requirement you cannot infer.
Blocks
A canvas can contain premade blocks: numbers, goals, trends, top lists, funnels, retention, saved insights, live event feeds, SQL tables, filters, compare and refresh controls, and callouts. They are React components in `src/blocks/` that people drag into the canvas in the desktop app, so they are part of your source. Read [references/blocks.md](references/blocks.md) before you change a canvas that has `src/blocks/runtime.tsx`.
Params
Expose the values a person may want to change as params. They can then change them in the desktop editor without a new task. Params are optional, but add them to each panel, card, or tool that has values worth changing: titles, copy, events, properties, thresholds, limits, colors. Params need React components. A plain HTML canvas has no params.
How the editor uses `editable()`
In the desktop editor, a person can select any element of the canvas: a heading, a paragraph, a section. They can move, duplicate, or delete it, and double-click text to change it. This works with no `editable()` call.
`editable()` makes a component one block. The editor then treats the block as one unit:
- A click selects the full block, not the element inside it.
- Move, duplicate, and delete act on the full block.
- The side panel shows the block's params as fields. A change writes the new value into the block's props in the source.
- To reach the elements inside a block, the person double-clicks the
Read more
name: building-canvases description: > Create or edit a PostHog freeform canvas — a sandboxed browser application (data board, document, form, small tool, graphics experiment) stored in PostHog and rendered by the desktop/web app. Use when a task asks to build, generate, update, or fix a standalone canvas app, or when a freeform canvas id is given as the publish target. For grid/home canvases, widget placements, or reusable components, use composing-grid-canvases instead. Covers resolving or creating the target canvas, choosing an implementation approach (React + Quill vs plain HTML/browser APIs), the read → edit → validate → publish → build loop, and which companion canvas skills to load for the details.
Building canvases
A canvas is a client-side browser application that runs in a sandboxed iframe inside PostHog. Its source lives in PostHog — not in a repository — and you read and write it through the `canvas-*` tools. Never write a canvas to a local file; publishing through the tool is what saves it.
Canvas work can start from any ordinary task. A dedicated canvas mode or pre-created canvas is not required. When the user asks for a board, document, form, visualization, or small app that should live in PostHog, treat that as a canvas request and follow this skill.
This skill owns `freeform` canvases (standalone apps). Two other canvas kinds exist: `grid` canvases (widget grids, including the user's home canvas) and `component` canvases (reusable widgets grids place). When the target is a grid or home canvas, a placement, or a reusable widget/component, load `composing-grid-canvases` instead — it owns the store search → configure → fork → build ladder and the layout patch loop. Authoring a component's source still uses the implementation companions below.
Resolve the target canvas
- If the task names a canvas id (canvas-initiated tasks do), that is the target. Do not create another.
- Otherwise the target channel is the one the task was created in — named in the task's context
(the `channel_context` block or the generation instructions). List that channel's canvases with `canvas-list` (scope with `channel`). If one is clearly what the request refers to — an earlier iteration of the same board or tool — build on it instead of creating a near-duplicate, and say so in your reply so the user knows where the result landed.
- Only when nothing existing fits, create one with `canvas-create` in that same channel, named
with a short descriptive title drawn from the request — never "Untitled canvas".
- Never survey channels to choose a target yourself: use `channel-list` only to resolve a channel
the USER named to its id. Its listing puts the personal #me channel first, and #me is never a default — a canvas filed there is invisible to everyone else. If the task names neither a canvas nor a channel, ask which channel to use instead of guessing.
Load the companion skills for the implementation
This skill owns canvas selection and the authoring lifecycle. The companion skills hold the implementation contracts. Load every companion that applies before writing source:
- **`building-react-quill-canvases`** for dashboards, data boards, forms, tools, application-like
state, or anything that should look native to PostHog. It owns allowed imports, Quill composition, theming, charts, loading and error states, and the date picker.
- **`building-html-canvases`** for documents, articles, focused experiments, generative graphics,
`<canvas>`, or WebGL work where application components add no useful structure. It owns semantic markup, direct browser APIs, animation cleanup, and non-Quill theming.
- **`querying-canvas-data`** whenever the canvas reads PostHog data, captures events, or navigates.
It owns the `ph` SDK, saved-insight preference, result shapes, variables, date ranges, progressive per-query loading, and declared data capabilities. Load it alongside either implementation skill when data is involved.
- **`validating-and-publishing-canvases`** for every canvas. It owns project shape, capability
declarations, validation diagnostics, guarded publishes, drafts, builds, and conflict recovery.
Mix implementation approaches when appropriate: React can own application chrome while browser graphics code owns a canvas element, or a mostly static page can mount one interactive island.
This is a judgment call, not a persisted mode — ask the user only when the choice changes a user-visible requirement you cannot infer.
Blocks
A canvas can contain premade blocks: numbers, goals, trends, top lists, funnels, retention, saved insights, live event feeds, SQL tables, filters, compare and refresh controls, and callouts. They are React components in `src/blocks/` that people drag into the canvas in the desktop app, so they are part of your source. Read [references/blocks.md](references/blocks.md) before you change a canvas that has `src/blocks/runtime.tsx`.
Params
Expose the values a person may want to change as params. They can then change them in the desktop editor without a new task. Params are optional, but add them to each panel, card, or tool that has values worth changing: titles, copy, events, properties, thresholds, limits, colors. Params need React components. A plain HTML canvas has no params.
How the editor uses `editable()`
In the desktop editor, a person can select any element of the canvas: a heading, a paragraph, a section. They can move, duplicate, or delete it, and double-click text to change it. This works with no `editable()` call.
`editable()` makes a component one block. The editor then treats the block as one unit:
- A click selects the full block, not the element inside it.
- Move, duplicate, and delete act on the full block.
- The side panel shows the block's params as fields. A change writes the new value into the block's props in the source.
- To reach the elements inside a block, the person double-clicks the
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 building-canvases.
- Which coding agents can use building-canvases?
- building-canvases 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.

