mcpbeat

Building React Quill Canvases

posthog/building-react-quill-canvases

> 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.

3k tokens
context cost
the whole folder, loaded on every use
2
files
instructions only
0
copies elsewhere
how many repositories repackaged it
690
stars on the repo
on the repository, not the skill itself

Install

one command, takes just this skill from the repository
npx skills add https://github.com/PostHog/posthog --skill building-react-quill-canvases

What comes with it

5 150 bytes besides the instruction
references/starter-scaffold.md

The instruction itself

6 sections, as written by the author

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

on a first build: it already wires the date picker, theme tokens, per-card skeletons, and correct

typed-node result reading. Keep that wiring; replace the sample metric and layout.

Imports

Import only from: react, react-dom, react-dom/client, @posthog/quill, recharts,

lucide-react, dayjs. Anything else — including dynamic import(), require(), fetch(),

<script> tags, or remote code — fails validation. Use @posthog/quill for UI, recharts for

charts, lucide-react for icons, dayjs for dates.

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.

  • 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

  • Style with Tailwind utilities and Quill components; reserve inline style for genuinely dynamic

runtime values (fixed sizes use arbitrary-value utilities like h-[280px]).

  • 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.
  • recharts strokes/fills use token CSS variables (stroke="var(--primary)", grid/axes in

var(--border)/var(--muted-foreground)).

  • Write Unicode glyphs (curly quotes, ellipsis, arrows, emoji) as literal characters in JSX —

\uXXXX escapes render verbatim in JSX text.

Loading states

Every data point renders a skeleton in its own Card while loading or refreshing: SkeletonText

(matching lines and text-size className) for text/number values, Skeleton for blocks/charts.

Drive isLoading off the data calls and set it true again on refresh; never show a blank or a

jumping layout, and handle the empty/error case.

Date window

A data board owns its own date control — render Quill's DateTimePicker (never a custom Select or

native date input) inside a Popover whose trigger is a Quill Button. PopoverContent gets

exactly className="w-auto p-0" and nothing is added to DateTimePicker beyond

value/onApply/onCancel (it self-sizes; don't pass compact or widths). Re-run every query

when the window changes — see the querying-canvas-data skill for feeding it into dateRange.

How to use it

Copy the folder

Take posthog/building-react-quill-canvases from the repository into ~/.claude/skills for personal use, or into .claude/skills inside a project.

Check the name does not clash

The agent identifies a skill by the name field in its header. Two skills with the same name cannot sit side by side — one of them will be ignored.