Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Add, manage, and compose shadcn/ui components with correct patterns, styling, and CLI workflows.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
rules/forms.md
1# Forms & Inputs23## Contents45- Forms use FieldGroup + Field6- InputGroup requires InputGroupInput/InputGroupTextarea7- Buttons inside inputs use InputGroup + InputGroupAddon8- Option sets (2–7 choices) use ToggleGroup9- FieldSet + FieldLegend for grouping related fields10- Field validation and disabled states1112---1314## Forms use FieldGroup + Field1516Always use `FieldGroup` + `Field` — never raw `div` with `space-y-*`:1718```tsx19<FieldGroup>20<Field>21<FieldLabel htmlFor="email">Email</FieldLabel>22<Input id="email" type="email" />23</Field>24<Field>25<FieldLabel htmlFor="password">Password</FieldLabel>26<Input id="password" type="password" />27</Field>28</FieldGroup>29```3031Use `Field orientation="horizontal"` for settings pages. Use `FieldLabel className="sr-only"` for visually hidden labels.3233**Choosing form controls:**3435- Simple text input → `Input`36- Dropdown with predefined options → `Select`37- Searchable dropdown → `Combobox`38- Native HTML select (no JS) → `native-select`39- Boolean toggle → `Switch` (for settings) or `Checkbox` (for forms)40- Single choice from few options → `RadioGroup`41- Toggle between 2–5 options → `ToggleGroup` + `ToggleGroupItem`42- OTP/verification code → `InputOTP`43- Multi-line text → `Textarea`4445---4647## InputGroup requires InputGroupInput/InputGroupTextarea4849Never use raw `Input` or `Textarea` inside an `InputGroup`.5051**Incorrect:**5253```tsx54<InputGroup>55<Input placeholder="Search..." />56</InputGroup>57```5859**Correct:**6061```tsx62import { InputGroup, InputGroupInput } from "@/components/ui/input-group"6364<InputGroup>65<InputGroupInput placeholder="Search..." />66</InputGroup>67```6869---7071## Buttons inside inputs use InputGroup + InputGroupAddon7273Never place a `Button` directly inside or adjacent to an `Input` with custom positioning.7475**Incorrect:**7677```tsx78<div className="relative">79<Input placeholder="Search..." className="pr-10" />80<Button className="absolute right-0 top-0" size="icon">81<SearchIcon />82</Button>83</div>84```8586**Correct:**8788```tsx89import { InputGroup, InputGroupInput, InputGroupAddon } from "@/components/ui/input-group"9091<InputGroup>92<InputGroupInput placeholder="Search..." />93<InputGroupAddon>94<Button size="icon">95<SearchIcon data-icon="inline-start" />96</Button>97</InputGroupAddon>98</InputGroup>99```100101---102103## Option sets (2–7 choices) use ToggleGroup104105Don't manually loop `Button` components with active state.106107**Incorrect:**108109```tsx110const [selected, setSelected] = useState("daily")111112<div className="flex gap-2">113{["daily", "weekly", "monthly"].map((option) => (114<Button115key={option}116variant={selected === option ? "default" : "outline"}117onClick={() => setSelected(option)}118>119{option}120</Button>121))}122</div>123```124125**Correct:**126127```tsx128import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group"129130<ToggleGroup spacing={2}>131<ToggleGroupItem value="daily">Daily</ToggleGroupItem>132<ToggleGroupItem value="weekly">Weekly</ToggleGroupItem>133<ToggleGroupItem value="monthly">Monthly</ToggleGroupItem>134</ToggleGroup>135```136137Combine with `Field` for labelled toggle groups:138139```tsx140<Field orientation="horizontal">141<FieldTitle id="theme-label">Theme</FieldTitle>142<ToggleGroup aria-labelledby="theme-label" spacing={2}>143<ToggleGroupItem value="light">Light</ToggleGroupItem>144<ToggleGroupItem value="dark">Dark</ToggleGroupItem>145<ToggleGroupItem value="system">System</ToggleGroupItem>146</ToggleGroup>147</Field>148```149150> **Note:** `defaultValue` and `type`/`multiple` props differ between base and radix. See [base-vs-radix.md](./base-vs-radix.md#togglegroup).151152---153154## FieldSet + FieldLegend for grouping related fields155156Use `FieldSet` + `FieldLegend` for related checkboxes, radios, or switches — not `div` with a heading:157158```tsx159<FieldSet>160<FieldLegend variant="label">Preferences</FieldLegend>161<FieldDescription>Select all that apply.</FieldDescription>162<FieldGroup className="gap-3">163<Field orientation="horizontal">164<Checkbox id="dark" />165<FieldLabel htmlFor="dark" className="font-normal">Dark mode</FieldLabel>166</Field>167</FieldGroup>168</FieldSet>169```170171---172173## Field validation and disabled states174175Both attributes are needed — `data-invalid`/`data-disabled` styles the field (label, description), while `aria-invalid`/`disabled` styles the control.176177```tsx178// Invalid.179<Field data-invalid>180<FieldLabel htmlFor="email">Email</FieldLabel>181<Input id="email" aria-invalid />182<FieldDescription>Invalid email address.</FieldDescription>183</Field>184185// Disabled.186<Field data-disabled>187<FieldLabel htmlFor="email">Email</FieldLabel>188<Input id="email" disabled />189</Field>190```191192Works for all controls: `Input`, `Textarea`, `Select`, `Checkbox`, `RadioGroupItem`, `Switch`, `Slider`, `NativeSelect`, `InputOTP`.193