Skip to content

Commit 9f33f2e

Browse files
feat(design-system): new Formspage in ui-patterns section (supabase#40978)
1 parent b410e6c commit 9f33f2e

File tree

6 files changed

+1635
-0
lines changed

6 files changed

+1635
-0
lines changed

apps/design-system/__registry__/index.tsx

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1831,6 +1831,28 @@ export const Index: Record<string, any> = {
18311831
subcategory: "undefined",
18321832
chunks: []
18331833
},
1834+
"form-patterns-pagelayout": {
1835+
name: "form-patterns-pagelayout",
1836+
type: "components:example",
1837+
registryDependencies: ["form","card","input","button","select","switch","checkbox","textarea","radio-group","calendar","popover","multi-select"],
1838+
component: React.lazy(() => import("@/registry/default/example/form-patterns-pagelayout")),
1839+
source: "",
1840+
files: ["registry/default/example/form-patterns-pagelayout.tsx"],
1841+
category: "undefined",
1842+
subcategory: "undefined",
1843+
chunks: []
1844+
},
1845+
"form-patterns-sidepanel": {
1846+
name: "form-patterns-sidepanel",
1847+
type: "components:example",
1848+
registryDependencies: ["form","sheet","input","button","select","switch","checkbox","textarea","radio-group","calendar","popover","multi-select"],
1849+
component: React.lazy(() => import("@/registry/default/example/form-patterns-sidepanel")),
1850+
source: "",
1851+
files: ["registry/default/example/form-patterns-sidepanel.tsx"],
1852+
category: "undefined",
1853+
subcategory: "undefined",
1854+
chunks: []
1855+
},
18341856
"form-item-layout-with-select": {
18351857
name: "form-item-layout-with-select",
18361858
type: "components:example",

apps/design-system/config/docs.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,11 @@ export const docsConfig: DocsConfig = {
6565
href: '/docs/ui-patterns/empty-states',
6666
items: [],
6767
},
68+
{
69+
title: 'Forms',
70+
href: '/docs/ui-patterns/forms',
71+
items: [],
72+
},
6873
],
6974
},
7075
{
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
---
2+
title: Forms
3+
description: Common form patterns used in Studio settings pages and side panels.
4+
---
5+
6+
Forms in Supabase Studio should follow consistent patterns to ensure a cohesive user experience across settings pages and side panels. This guide covers the most common form patterns and field types.
7+
8+
## Page Layout
9+
10+
Forms in page layouts typically use `PageSection` components with `Card` containers. Fields use `FormItemLayout` with `layout="flex-row-reverse"` for horizontal alignment.
11+
12+
<ComponentPreview
13+
name="form-patterns-pagelayout"
14+
description="Complete form example with all field types in a PageLayout pattern"
15+
peekCode
16+
wide
17+
/>
18+
19+
## Side Panel
20+
21+
Forms in side panels (Sheets) use `FormItemLayout` with `layout="horizontal"` on wider panels and `layout="vertical"` on panels with a size of `sm` or below. The form is typically wrapped in a `Sheet` component.
22+
23+
<ComponentPreview
24+
name="form-patterns-sidepanel"
25+
description="Complete form example with all field types in a SidePanel/Sheet pattern"
26+
peekCode
27+
wide
28+
/>
29+
30+
## Best Practices
31+
32+
1. **Always use FormItemLayout**: Use `FormItemLayout` instead of manually composing `FormItem`, `FormLabel`, `FormMessage`, and `FormDescription`.
33+
34+
2. **Layout selection**:
35+
36+
- Use `layout="flex-row-reverse"` for page layouts (horizontal alignment)
37+
- Use `layout="horizontal"` for side panels with more width
38+
- Use `layout="vertical"` for side panels with limited width
39+
40+
3. **Wrap inputs in FormControl*Shadcn***: Always wrap form inputs with `FormControl_Shadcn_` to ensure proper form integration.
41+
42+
4. **Use Cards for grouping**: Wrap form sections in `Card` components with `CardContent` and `CardFooter` for actions.
43+
44+
5. **Handle dirty state**: Show cancel buttons and disable save buttons based on `form.formState.isDirty`.
45+
46+
6. **Error handling**: Always use mutations with `onSuccess` and `onError` callbacks that show toast notifications.
47+
48+
7. **Loading states**: Show loading states on submit buttons using the `loading` prop.
49+
50+
8. **Form IDs**: When submit buttons are outside the form, use a form ID and reference it with the `form` prop on the button.

0 commit comments

Comments
 (0)