Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Design system UI patterns for Supabase Studio. Use when building or updating pages, forms, tables, charts, empty states, navigation, cards, alerts, or side panels (sheets). Covers layout selection, component choice, and placement conventions.
.claude/skills/supabase-studio-ui-patterns/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | -11% | 0% |
| case-02 | ✗→✓ | ▲ Improved | -13% | 0% |
| case-09 | ✗→✓ | ▲ Improved | 19% | 0% |
| case-10 | ✗→✓ | ▲ Improved | -27% | 0% |
| case-15 | ✗→✓ | ▲ Improved | -17% | 0% |
The Design System docs and demos are the source of truth. Always check the relevant demo file before composing new UI.
Docs: apps/design-system/content/docs/ui-patterns/layout.mdx
Build pages with PageContainer, PageHeader, and PageSection.
| Content type | size | | ----------------- | ----------- | | Settings / config | "default" | | Lists / tables | "large" | | Full-screen views | "full" |
PageHeaderAside/PageSectionAside for those actions)PageHeaderAside or PageSectionAsideDemos: page-layout-settings.tsx, page-layout-list.tsx, page-layout-list-simple.tsx, page-layout-detail.tsx (all in apps/design-system/registry/default/example/)
Docs: apps/design-system/content/docs/ui-patterns/forms.mdx
react-hook-form + zodFormItemLayout instead of manually composing FormItem/FormLabel/FormMessage/FormDescriptionFormControl; import primitives from uiLayout selection:
| Context | Layout | Container | | ------------------------------------------ | ------------------------------------------ | ---------------------------------------------------------- | | Page (settings/config) | FormItemLayout layout="flex-row-reverse" | Card (CardContent per field; CardFooter for actions) | | Side panel — wide | FormItemLayout layout="horizontal" | SheetSection | | Side panel — narrow (size="sm" or below) | FormItemLayout layout="vertical" | SheetSection |
Dirty state / submit:
isDirty to show Cancel and disable Save. Destructure it from form.formState only in the component that owns useForm; anywhere else subscribe with useFormState({ control }) (see the react-hook-form skill)loading prop<form>, set a stable formId and use form prop on the buttonDemos: form-patterns-pagelayout.tsx, form-patterns-sidepanel.tsx
Docs: apps/design-system/content/docs/ui-patterns/tables.mdx
| Pattern | Use when | | ---------- | ----------------------------------------------------------------------- | | Table | Simple, static, semantic display | | Data Table | TanStack-powered; sorting, filtering, pagination; composed per use-case | | Data Grid | Virtualization, column resizing, or complex cell editing |
Demos: table-demo.tsx, data-table-demo.tsx, data-grid-demo.tsx
Docs: apps/design-system/content/docs/ui-patterns/charts.mdx
ChartContentuseChart context flags for loading/disabled statesDemos (in apps/design-system/registry/default/block/): chart-composed-demo.tsx, chart-composed-basic.tsx, chart-composed-states.tsx, chart-composed-metrics.tsx, chart-composed-actions.tsx, chart-composed-table.tsx
Docs: apps/design-system/content/docs/ui-patterns/empty-states.mdx
| Scenario | Pattern | | ------------------------ | ------------------------------------------------------------------- | | Initial / onboarding | Presentational empty state with value prop + clear next action | | Data-heavy lists | Informational empty state matching the list/table layout | | Zero results from search | Keep layout consistent with data state to avoid jarring transitions | | Missing route | Centered Admonition |
Demos: empty-state-presentational-icon.tsx, empty-state-initial-state-informational.tsx, empty-state-zero-items-table.tsx, data-grid-empty-state.tsx, empty-state-missing-route.tsx
Docs: apps/design-system/content/docs/ui-patterns/navigation.mdx
NavMenu for a horizontal list of related views within a consistent page layoutCardContent for sections, CardFooter for actionsCardHeader/CardTitle when context isn't already provided by surrounding contentAdmonition to call out important actions, restrictions, or critical contextUse a Sheet when switching pages would be disruptive and the user needs to maintain context (e.g. selecting a row from a list to edit).
Structure:
SheetContent with size="lg" for forms needing horizontal layoutSheetHeader, SheetTitle, SheetSection, SheetFooterSheetFooterForms in sheets:
layout="horizontal" for wider sheetslayout="vertical" for narrow sheets (size="sm" or below)useConfirmOnClose +DiscardChangesConfirmationDialog (Cancel, Escape, and backdrop). Source of truth: apps/design-system/content/docs/ui-patterns/modality.mdx (Dirty form dismissal). Also see the react-hook-form skill for isDirty destructuring.
Source of truth: apps/design-system/content/docs/copywriting.mdx — sentence case, title case, proper nouns, voice and tone.
When changing visible copy, grep e2e/studio/ for the old string.
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→pass | 31,138 | 18,857 | -39% | 1 | 1 | 0% | 6,544 | 5,835 | -11% | 0 | 0 | — |
case-02 | fail→pass | 30,741 | 20,305 | -34% | 1 | 1 | 0% | 6,590 | 5,708 | -13% | 0 | 0 | — |
case-03 | fail→fail | 38,558 | 37,709 | -2% | 1 | 1 | 0% | 8,253 | 9,811 | +19% | 0 | 0 | — |
case-04 | pass→pass | 14,363 | 9,291 | -35% | 1 | 1 | 0% | 2,359 | 3,014 | +28% | 0 | 0 | — |
case-05 | pass→pass | 14,264 | 9,774 | -31% | 1 | 1 | 0% | 2,799 | 3,245 | +16% | 0 | 0 | — |
case-06 | fail→fail | 17,562 | 16,320 | -7% | 1 | 1 | 0% | 3,264 | 4,942 | +51% | 0 | 0 | — |
case-07 | pass→pass | 13,920 | 3,070 | -78% | 1 | 1 | 0% | 1,886 | 1,878 | -0% | 0 | 0 | — |
case-08 | pass→pass | 10,161 | 4,295 | -58% | 1 | 1 | 0% | 1,481 | 2,072 | +40% | 0 | 0 | — |
case-09 | fail→pass | 11,576 | 4,093 | -65% | 1 | 1 | 0% | 1,792 | 2,133 | +19% | 0 | 0 | — |
case-10 | fail→pass | 16,969 | 3,476 | -80% | 1 | 1 | 0% | 2,804 | 2,049 | -27% | 0 | 0 | — |
case-11 | pass→pass | 11,696 | 7,671 | -34% | 1 | 1 | 0% | 2,375 | 2,706 | +14% | 0 | 0 | — |
case-12 | pass→pass | 14,252 | 9,388 | -34% | 1 | 1 | 0% | 2,212 | 3,238 | +46% | 0 | 0 | — |
case-13 | pass→pass | 8,279 | 3,035 | -63% | 1 | 1 | 0% | 1,196 | 1,904 | +59% | 0 | 0 | — |
case-14 | pass→fail | 14,091 | 4,301 | -69% | 1 | 1 | 0% | 2,119 | 2,082 | -2% | 0 | 0 | — |
case-15 | fail→pass | 23,191 | 9,899 | -57% | 1 | 1 | 0% | 3,541 | 2,924 | -17% | 0 | 0 | — |
case-16 | pass→pass | 10,631 | 4,897 | -54% | 1 | 1 | 0% | 1,594 | 2,201 | +38% | 0 | 0 | — |
case-17 | fail→pass | 15,883 | 125,925 | +693% | 1 | 1 | 0% | 2,798 | 2,094 | -25% | 0 | 0 | — |
case-18 | pass→pass | 14,144 | 5,229 | -63% | 1 | 1 | 0% | 2,240 | 2,169 | -3% | 0 | 0 | — |
case-19 | fail→pass | 14,879 | 3,317 | -78% | 1 | 1 | 0% | 2,059 | 2,082 | +1% | 0 | 0 | — |
case-20 | pass→pass | 12,238 | 3,565 | -71% | 1 | 1 | 0% | 1,775 | 2,001 | +13% | 0 | 0 | — |
case-21 | fail→pass | 9,886 | 2,814 | -72% | 1 | 1 | 0% | 1,355 | 1,906 | +41% | 0 | 0 | — |
case-22 | pass→pass | 15,115 | 7,000 | -54% | 1 | 1 | 0% | 2,484 | 2,890 | +16% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +32 percentage points is the difference between those two pass rates over the 22 comparable cases. 2 cases got worse with the skill loaded, and they are included in that figure.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.