supabase/studio-ui-patterns
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.
npx skills add https://github.com/supabase/supabase --skill studio-ui-patterns
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 from form.formState to show Cancel and disable Saveloading 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.
Take supabase/studio-ui-patterns from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
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.