React Form Components
15+ accessible form components: inputs, selects, checkboxes, radio groups, sliders, textareas, file uploads. All typed, all Tailwind v4 styled.
import { Input, Select, Checkbox, Switch } from "@ninna-ui/forms";
import { Field } from "@ninna-ui/forms";
<Field label="Email" htmlFor="email">
<Input id="email" type="email" placeholder="you@example.com" />
</Field>
<Select>
<Select.Item value="us">United States</Select.Item>
<Select.Item value="ca">Canada</Select.Item>
</Select>
<Checkbox>Accept terms</Checkbox>
<Switch>Enable notifications</Switch>Key features
15+ form components: Input, Select, Checkbox, Radio, Switch, Slider, Textarea, FileUpload, PinInput, NumberInput
Field wrapper with label, description, and error message
InputGroup with startElement / endElement for icons
Checkbox and Switch use onCheckedChange (not onChange)
Full ARIA compliance with Radix UI internals
FormControl for programmatic validation state
Complete form coverage
Ninna UI's @ninna-ui/forms package covers every common form control: text inputs, selects, checkboxes, radio groups, switches, sliders, textareas, file uploads, pin inputs, and number inputs. Each component is accessible, typed, and styled with Tailwind CSS v4.
Field wrapper pattern
The Field component wraps any input with a label, description, and error message — handling ARIA wiring automatically:
<Field label="Username" htmlFor="username" error="Username is taken" invalid>
<Input id="username" placeholder="Choose a username" />
</Field>
<Field label="Bio" description="Tell us about yourself" htmlFor="bio">
<Textarea id="bio" rows={4} />
</Field>InputGroup for icons
Put icons inside inputs using the startElement and endElement props on InputGroup — not a leftIcon prop on Input:
import { InputGroup } from "@ninna-ui/forms";
import { Search } from "lucide-react";
<InputGroup startElement={<Search className="size-4" />}>
<Input placeholder="Search…" />
</InputGroup>