/building-react-quill-canvases
Author the React + Quill implementation of a PostHog canvas: the single-component contract, the allowed imports, Quill (PostHog's design system) component and composition rules, theme-aware design tokens, loading skeletons, and the in-canvas date picker. Use after
Claude Code
$ npx -y skills add PostHog/ai-plugin --skill building-react-quill-canvases --agent claude-code
Codex
$ npx skills add PostHog/ai-plugin --skill building-react-quill-canvases -a codex -y
Cursor
$ npx skills add PostHog/ai-plugin --skill building-react-quill-canvases -a cursor -y
Gemini CLI
$ npx skills add PostHog/ai-plugin --skill building-react-quill-canvases -a gemini-cli -y
GitHub Copilot
$ npx skills add PostHog/ai-plugin --skill building-react-quill-canvases -a github-copilot -y
OpenCode
$ npx skills add PostHog/ai-plugin --skill building-react-quill-canvases -a opencode -y
Windsurf
$ npx skills add PostHog/ai-plugin --skill building-react-quill-canvases -a windsurf -y
SKILL.md
building-react-quill-canvases.SKILL.mdname: building-react-quill-canvases
description: >
Author the React + Quill implementation of a PostHog canvas: the single-component contract, the
allowed imports, Quill (PostHog's design system) component and composition rules, theme-aware
design tokens, loading skeletons, and the in-canvas date picker. Use after building-canvases has
routed a canvas request to a React implementation — dashboards, data boards, forms, tools, or any
canvas that should look native to PostHog.
Building React + Quill canvases
The whole application is one React/TSX file (`src/canvas.tsx` in the source project). It must `export default` a single React component that takes no props — the host mounts it. Do not import react-dom or call createRoot.
Start from the working scaffold in [references/starter-scaffold.md](references/starter-scaffold.md) on a first build: it already wires the date picker, theme tokens, per-query loading state (every card fills in independently as its own data lands), and correct typed-node result reading. Keep that wiring; replace the sample metrics and layout. Keep its pattern of one component per card with `editable()` params, and follow the "Params" section of the `building-canvases` skill for each card you add.
Imports
Use React, Quill, Recharts, Lucide, and Day.js for the standard application shell. The platform also admits ten optional libraries for specialized work. Read [references/platform-libraries.md](references/platform-libraries.md) before choosing one.
PostHog data comes through `import { ph } from "@posthog/canvas-sdk"` — a platform-provided module, so it needs no `dependencies` entry. The same object exists as the `window.ph` global (how existing canvases reach it); prefer the import in new code.
Other bare imports, dynamic `import()`, `require()`, `<script>` tags, and remote code fail validation. Direct network requests and external images, fonts, media, or frames require an exact HTTPS origin in `capabilities.network.origins`. They work only in the **published** canvas — the edit-mode preview blocks direct network access regardless of declaration. Stylesheets from declared origins are allowed; remote scripts remain blocked, so bundle code with the canvas.
Quill component rules
A PostHog data board must be built entirely from `@posthog/quill` components — never a native control or a styled `<div>` standing in for one:
- Dropdown/picker → `Select` (never a native `<select>`); button → `Button` (never `<button>`);
text field → `Input`/`Textarea`; checkbox → `Checkbox`; label → `Label`.
- Table → `Table` (`TableHeader` > `TableRow` > `TableHead`, then `TableBody` > `TableRow` > `TableCell`);
panel → `Card` (`CardHeader` + `CardTitle` + `CardContent`); pill → `Badge`; titles → `Heading`; body → `Text`.
- A `Table` inside a `Card` needs `<Card size="sm" flush>` around `<Table size="sm" fullWidth>`.
Without `flush` the card's padding insets the table, so its header and rows stop meeting the card's edges. `flush` drops the card's section gap, bottom padding, and the `CardContent` inline padding, while the header keeps its own padding and divider. Matching `size="sm"` on both lines the table's edge columns up with the card's title.
- The only non-Quill tags allowed are plain layout `<div>`s and `recharts` elements.
- Quill is built on Base UI: compose compound parts (`Select` + `SelectTrigger`/`SelectContent`/`SelectItem`),
use controlled `value` + `onValueChange`, and swap a part's element with the `render` prop (e.g. `<PopoverTrigger render={<Button …/>} />`) instead of wrapping it.
- Quill components are already themed — never restyle one with Tailwind classes or inline `style`;
use their `variant`/`size` props. Put layout utilities (`flex`, `grid`, `gap-4`, `p-4`) on your own wrapper `<div>`s.
- Buttons: default to `variant="outline"`; `variant="primary"` for the one main action only.
Styling and theme
- Give the canvas's outermost element `min-h-screen` (`min-height: 100vh`) so it fills the iframe viewport and grows past it as content demands.
Do not use `h-screen` there: a fixed viewport height caps a flex column, so tall children shrink and clip instead of scrolling. Do not use `h-full` there either: a published canvas's artifact shell gives its `html`, `body`, and `#root` elements no explicit height, so a percentage root height collapses to content height.
- The root needs no `overflow-y-auto`: once it grows past the viewport the iframe's own document scrolls.
- A `min-height` root is not a definite height, so `h-full` on a direct child still collapses to content height.
Give an intermediate wrapper an explicit height (`h-[280px]`) when a child must fill a box.
- Style with Tailwind utilities and Quill components; reserve inline `style` for genuinely dynamic
runtime values (fixed sizes use arbitrary-value utilities like `h-[280px]`).
- Write specific interface copy. Never use lorem ipsum or placeholder labels in a finished canvas.
- The canvas follows the user's PostHog theme; a `.dark` class on the document root flips at runtime.
Color only from the design-token utilities — surfaces `bg-background bg-card bg-muted bg-primary bg-success bg-warning bg-info bg-destructive`; text `text-foreground text-muted-foreground text-card-foreground`; borders `border-border`. Never a hardcoded hex or light-only color.
- Status tokens invert the usual convention: the bare token (`bg-success`) is a pale background fill
and `-foreground` (`text-success-foreground`) is the strong readable color. Colored text or icons always use the `-foreground` utility; a filled pill pairs `bg-success text-success-foreground`. Prefer the Quill `Badge` (`variant="success"`/`"destructive"`) for deltas so you don't hand-pick.
- `bg-secondary`, `text-secondary`, `bg-accent`, and `bg-popover` are not defined in the canvas — avoid them.
- Never declare a CSS variable with a platform token name (`--background`, `--border`, `--card`,
`-
Read more
name: building-react-quill-canvases description: > Author the React + Quill implementation of a PostHog canvas: the single-component contract, the allowed imports, Quill (PostHog's design system) component and composition rules, theme-aware design tokens, loading skeletons, and the in-canvas date picker. Use after building-canvases has routed a canvas request to a React implementation — dashboards, data boards, forms, tools, or any canvas that should look native to PostHog.
Building React + Quill canvases
The whole application is one React/TSX file (`src/canvas.tsx` in the source project). It must `export default` a single React component that takes no props — the host mounts it. Do not import react-dom or call createRoot.
Start from the working scaffold in [references/starter-scaffold.md](references/starter-scaffold.md) on a first build: it already wires the date picker, theme tokens, per-query loading state (every card fills in independently as its own data lands), and correct typed-node result reading. Keep that wiring; replace the sample metrics and layout. Keep its pattern of one component per card with `editable()` params, and follow the "Params" section of the `building-canvases` skill for each card you add.
Imports
Use React, Quill, Recharts, Lucide, and Day.js for the standard application shell. The platform also admits ten optional libraries for specialized work. Read [references/platform-libraries.md](references/platform-libraries.md) before choosing one.
PostHog data comes through `import { ph } from "@posthog/canvas-sdk"` — a platform-provided module, so it needs no `dependencies` entry. The same object exists as the `window.ph` global (how existing canvases reach it); prefer the import in new code.
Other bare imports, dynamic `import()`, `require()`, `<script>` tags, and remote code fail validation. Direct network requests and external images, fonts, media, or frames require an exact HTTPS origin in `capabilities.network.origins`. They work only in the **published** canvas — the edit-mode preview blocks direct network access regardless of declaration. Stylesheets from declared origins are allowed; remote scripts remain blocked, so bundle code with the canvas.
Quill component rules
A PostHog data board must be built entirely from `@posthog/quill` components — never a native control or a styled `<div>` standing in for one:
- Dropdown/picker → `Select` (never a native `<select>`); button → `Button` (never `<button>`);
text field → `Input`/`Textarea`; checkbox → `Checkbox`; label → `Label`.
- Table → `Table` (`TableHeader` > `TableRow` > `TableHead`, then `TableBody` > `TableRow` > `TableCell`);
panel → `Card` (`CardHeader` + `CardTitle` + `CardContent`); pill → `Badge`; titles → `Heading`; body → `Text`.
- A `Table` inside a `Card` needs `<Card size="sm" flush>` around `<Table size="sm" fullWidth>`.
Without `flush` the card's padding insets the table, so its header and rows stop meeting the card's edges. `flush` drops the card's section gap, bottom padding, and the `CardContent` inline padding, while the header keeps its own padding and divider. Matching `size="sm"` on both lines the table's edge columns up with the card's title.
- The only non-Quill tags allowed are plain layout `<div>`s and `recharts` elements.
- Quill is built on Base UI: compose compound parts (`Select` + `SelectTrigger`/`SelectContent`/`SelectItem`),
use controlled `value` + `onValueChange`, and swap a part's element with the `render` prop (e.g. `<PopoverTrigger render={<Button …/>} />`) instead of wrapping it.
- Quill components are already themed — never restyle one with Tailwind classes or inline `style`;
use their `variant`/`size` props. Put layout utilities (`flex`, `grid`, `gap-4`, `p-4`) on your own wrapper `<div>`s.
- Buttons: default to `variant="outline"`; `variant="primary"` for the one main action only.
Styling and theme
- Give the canvas's outermost element `min-h-screen` (`min-height: 100vh`) so it fills the iframe viewport and grows past it as content demands.
Do not use `h-screen` there: a fixed viewport height caps a flex column, so tall children shrink and clip instead of scrolling. Do not use `h-full` there either: a published canvas's artifact shell gives its `html`, `body`, and `#root` elements no explicit height, so a percentage root height collapses to content height.
- The root needs no `overflow-y-auto`: once it grows past the viewport the iframe's own document scrolls.
- A `min-height` root is not a definite height, so `h-full` on a direct child still collapses to content height.
Give an intermediate wrapper an explicit height (`h-[280px]`) when a child must fill a box.
- Style with Tailwind utilities and Quill components; reserve inline `style` for genuinely dynamic
runtime values (fixed sizes use arbitrary-value utilities like `h-[280px]`).
- Write specific interface copy. Never use lorem ipsum or placeholder labels in a finished canvas.
- The canvas follows the user's PostHog theme; a `.dark` class on the document root flips at runtime.
Color only from the design-token utilities — surfaces `bg-background bg-card bg-muted bg-primary bg-success bg-warning bg-info bg-destructive`; text `text-foreground text-muted-foreground text-card-foreground`; borders `border-border`. Never a hardcoded hex or light-only color.
- Status tokens invert the usual convention: the bare token (`bg-success`) is a pale background fill
and `-foreground` (`text-success-foreground`) is the strong readable color. Colored text or icons always use the `-foreground` utility; a filled pill pairs `bg-success text-success-foreground`. Prefer the Quill `Badge` (`variant="success"`/`"destructive"`) for deltas so you don't hand-pick.
- `bg-secondary`, `text-secondary`, `bg-accent`, and `bg-popover` are not defined in the canvas — avoid them.
- Never declare a CSS variable with a platform token name (`--background`, `--border`, `--card`,
`-
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-react-quill-canvases.
- Which coding agents can use building-react-quill-canvases?
- building-react-quill-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.

