Skip to content
Development
Skill

/composing-grid-canvases

Compose PostHog grid canvases — widget grids (including the user's home canvas) built from reusable component canvases. Use when a task asks to add, fill, move, resize, or remove a widget on a grid or home canvas, to compose a whole canvas of widgets from one ask, to build a

From plugin
ai-plugin
95180 skills1 agent3 commands3 hooks
+1

Claude Code

$ npx -y skills add PostHog/ai-plugin --skill composing-grid-canvases --agent claude-code

SKILL.md

composing-grid-canvases.SKILL.md
name: composing-grid-canvases
description: >
  Compose PostHog grid canvases — widget grids (including the user's home canvas) built from
  reusable component canvases. Use when a task asks to add, fill, move, resize, or remove a widget
  on a grid or home canvas, to compose a whole canvas of widgets from one ask, to build a reusable
  widget/component, or when a placement id or grid canvas id is the target. Covers the component
  store search → configure → fork → build ladder, the component placement contract (size,
  configSchema), the placement lifecycle (pending/generating/live/failed), the guarded layout
  patch loop, and reading the canvas's comment threads.

Composing grid canvases

A grid canvas is a composition, not an app: a grid of placements, each rendering a **component canvas** (a reusable widget with its own source, build, and placement contract). The user's home canvas is an ordinary grid canvas in their personal channel. Layout is data — publishing or patching one is live immediately, with no build.

Three canvas kinds share one lifecycle:

  • `freeform` — a standalone app (the `building-canvases` skill owns these).
  • `component` — a reusable widget. Same source/build pipeline as freeform, plus a placement

contract. Visibility rides its channel: personal channel = private, team channel = shared.

  • `grid` — a layout of placements referencing components. No file source; layout only.

The resolution ladder: configure, fork, build

When a grid placement needs content ("a weather widget here", "a kanban of my tasks"), resolve in this order — placing an existing component beats authoring a duplicate:

1. **Search the store**: `canvas-list` with `kind=component` and `search=<what the widget shows>`. A component is placeable when both `component_meta` and `published_build_id` are set. If its `configSchema` can express the request ("weather for Lisbon" → existing weather component with `config: {"location": "Lisbon"}`), place and configure it — write no code. 2. **Fork** when a component is close but its config cannot express the ask: read its source (`canvas-source-retrieve`), create a new component (`canvas-create` with `kind=component`), adapt, publish. Name the difference in the new component's description. 3. **Build new** when nothing fits — see "Building a component" below.

New components land in the channel you create them in. Create them in the same channel as the grid they serve unless the user asks to share them more widely.

Building a component

A component is authored exactly like a freeform canvas — load `building-react-quill-canvases` (or `building-html-canvases`) plus `querying-canvas-data` and `validating-and-publishing-canvases` — with three additions. A component that shows PostHog data follows the same verifiability rule as any canvas: an insight-backed figure links its saved insight, an ad-hoc query exposes the exact query that ran (see "Verifiability" in `querying-canvas-data`). Start from the complete, buildable project in [references/component-example.md](references/component-example.md); its envelope, placement contract, capability declarations, and defensive `ph.state` access are the parts that break when improvised.

  • **Create with `kind=component`** and a `description` written for store search: say what the

widget shows and what its config controls. Future placements are found by this text.

  • **Declare the placement contract** in the project's top-level `component` key:
  {
    "component": {
      "size": { "defaultW": 2, "defaultH": 1, "minW": 1, "minH": 1, "maxW": 4 },
      "configSchema": {
        "type": "object",
        "properties": { "location": { "type": "string", "description": "City to show weather for" } }
      }
    }
  }

Size is in grid units (widths 1–12, heights 1–40); `minW <= defaultW <= maxW`. The range is advisory: users may resize a placement to any size, so it informs defaults and warnings, never rejections. The config schema vocabulary is an allowlist — `type`, `title`, `description`, `default`, `properties`, `required`, `additionalProperties`, `items`, `enum`, `const`, `minimum`, `maximum`, `minLength`, `maxLength`, `minItems`, `maxItems`, `format`. No `$ref`, no `pattern` — validation rejects them.

  • **Design responsively.** Give the component's root `min-h-screen` so it fills the placement iframe's viewport and grows past it as content demands.

Adapt the layout to any size the user drags: a 2×1 placement is a glanceable tile; a 6×4 is a full app surface. Do not use `h-screen` on the root: a fixed viewport height caps a flex column, so tall children shrink and clip instead of scrolling. Do not use `h-full` on the root either: a published component's artifact shell gives its `html`, `body`, and `#root` elements no explicit height, so `height: 100%` collapses to the content height. Render usefully at `minW`×`minH`, and treat `config` as the only per-placement input.

Publish and wait for the build like any canvas — a component with no ready build cannot go live on a grid.

Composing a whole canvas

A whole-canvas ask ("a home canvas that summarizes my work in progress") usually means several widgets, not one. Plan the full set first — one placement per concern — then resolve each with the ladder above. Lay them out together: no overlaps, sizes matched to what each widget shows, the grid filled deliberately rather than tiles scattered in a corner. Batch the layout writes (one publish for an initial layout, surgical patches after) instead of one write per widget, and finish with every placement live or failed — never generating.

Canvas comments

Users leave feedback as comment threads on the canvas. Run `exec search ^canvas-comments-(list|retrieve)$` first. If both tools are present, list the threads with them before and after changing the canvas, and address the open ones — a comment naming a broken widget is your brief for fixing it. These tool

Read more
@posthog
Ships withai-plugin

Official PostHog plugin for AI clients. Access PostHog products directly from your AI coding tool.

95180 skills1 agent1 MCP
Get the whole plugin

Stars on GitHub

Momentum

stars in total
95
Star history of PostHog/ai-plugin on GitHub, a chart by star-history.com

Other skills on ai-plugin.

analyzing-ai-citations

analyzing-ai-citations

Answer AI citation and answer engine optimization (AEO) questions in PostHog: whether AI answer engines (ChatGPT, Claude, and an…

@posthog@posthogInstall Skill

More skills from other plugins.

brainstorming
Auto-invoked

brainstorming

You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…

@obra@obraInstall Skill
ask-matt

ask-matt

Ask which skill or flow fits your situation. A router over the skills in this repo.

skills10d309k
@mattpocock@mattpocockInstall Skill
accessibility
Auto-invoked

accessibility

Design, implement, and audit accessible UI to WCAG 2.2 Level AA across Web, iOS, and Android — semantic ARIA roles and labels,…

ecc12d293k
@affaan-m@affaan-mInstall Skill
compiler-commit

compiler-commit

Use when you want to verify compiler changes and commit with the correct convention. Runs tests, lint, and format, then commits…

react1d252k
@facebook@facebookInstall Skill

Questions about composing-grid-canvases.

Which coding agents can use composing-grid-canvases?
composing-grid-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.