Form components
Input and form control components for user interaction
Form components handle user input and form submission. All components are built with accessibility in mind and support keyboard navigation.
The category splits into three groups:
- Controls: raw inputs you can drop in standalone or compose inside
<Form> - Composition: the
<Form>provider and the low-level building blocks (FormField,FormItem,FormLabel,FormControl,FormDescription,FormMessage) - Prebuilt fields: one-tag wrappers that bind a control to React hook form
Prerequisites
- Complete the @mdk/core installation and add the dependency
Controls
Standalone form controls. Manage value and onChange yourself, or compose them inside <Form> with FormField.
| Component | Description |
|---|---|
Button | Primary action trigger with variants and sizes |
ActionButton | Button with loading state and icon support |
Input | Text input field with label and validation |
TextArea | Multi-line text input |
Select | Dropdown single/multi-select input |
Cascader | Multi-level dropdown selector for hierarchical data |
Checkbox | Binary toggle input for forms |
Switch | Toggle for on/off settings |
Radio | Single-select option from a group |
DatePicker | Calendar-based date selection input |
DateRangePicker | Two-month date range selector with presets and apply/clear actions |
Slider | Range input for numeric values |
Label | Form field label with optional required indicator |
TagInput | Input for entering multiple tags |
ActionButton
Button with loading state and icon support for async actions.
Import
import { ActionButton } from '@mdk/core'Basic usage
<ActionButton onClick={handleAction}>Save</ActionButton>
<ActionButton loading onClick={handleAction}>Processing</ActionButton>
<ActionButton icon={<CheckIcon />} onClick={handleAction}>Approve</ActionButton>Button
Configurable button with multiple variants, sizes, and loading state.
Import
import { Button } from '@mdk/core'Props
| Prop | Type | Default | Description |
|---|---|---|---|
variant | 'primary' | 'secondary' | 'danger' | 'tertiary' | 'link' | 'icon' | 'outline' | 'ghost' | 'secondary' | Visual variant |
size | 'sm' | 'md' | 'lg' | none | Button size |
loading | boolean | false | Show loading spinner |
fullWidth | boolean | false | Expand to container width |
icon | ReactNode | none | Icon element |
iconPosition | 'left' | 'right' | 'left' | Icon placement |
disabled | boolean | false | Disable interaction |
Basic usage
<Button>Default Button</Button>
<Button variant="primary">Primary</Button>
<Button variant="danger">Delete</Button>
<Button variant="outline">Outline</Button>With icon
<Button icon={<PlusIcon />}>Add Item</Button>
<Button icon={<ArrowRightIcon />} iconPosition="right">
Continue
</Button>Loading state
<Button loading>Saving...</Button>
<Button variant="primary" loading disabled>
Processing
</Button>Styling
.mdk-button: Root element.mdk-button--variant-{variant}: Variant modifier.mdk-button--size-{size}: Size modifier.mdk-button--full-width: Full width modifier.mdk-button--loading: Loading state
Cascader
Multi-level dropdown selector for hierarchical data.
Import
import { Cascader } from '@mdk/core'Basic usage
<Cascader
options={[
{
value: 'na',
label: 'North America',
children: [
{ value: 'us-east', label: 'US East' },
{ value: 'us-west', label: 'US West' },
],
},
]}
value={value}
onChange={setValue}
/>Checkbox
Checkbox input built on Radix UI primitives.
Import
import { Checkbox } from '@mdk/core'Props
| Prop | Type | Default | Description |
|---|---|---|---|
size | 'xs' | 'sm' | 'md' | 'lg' | 'md' | Checkbox size |
color | 'default' | 'primary' | 'success' | 'warning' | 'error' | 'primary' | Color when checked |
radius | 'none' | 'small' | 'medium' | 'large' | 'full' | 'none' | Border radius |
checked | boolean | 'indeterminate' | none | Checked state (controlled) |
onCheckedChange | function | none | Change callback |
Basic usage
<Checkbox checked={checked} onCheckedChange={setChecked} />
<label className="flex items-center gap-2">
<Checkbox checked={terms} onCheckedChange={setTerms} />
I agree to the terms
</label>Sizes and colors
<Checkbox size="xs" />
<Checkbox size="sm" />
<Checkbox size="md" />
<Checkbox size="lg" color="success" />Styling
.mdk-checkbox: Root element.mdk-checkbox--{size}: Size modifier.mdk-checkbox--{color}: Color modifier.mdk-checkbox__indicator: Check mark container
DatePicker
Calendar-based date selection input.
Import
import { DatePicker } from '@mdk/core'Basic usage
<DatePicker
value={date}
onChange={setDate}
placeholder="Select date"
/>DateRangePicker
Two-month date range selector built on react-day-picker. Renders a popover modal with presets (Last 7/14/30/90 Days by default), a draft selection summary, and explicit apply/clear actions. Future dates are disabled by default.
Import
import { DateRangePicker } from '@mdk/core'
import type { DateRange, PresetItem } from '@mdk/core'Props
| Prop | Type | Default | Description |
|---|---|---|---|
selected | DateRange | none | Selected { from, to } range (controlled) |
onSelect | (range: DateRange | undefined) => void | none | Called when the user applies a range |
placeholder | string | 'Pick a date range' | Trigger text when no range is selected |
dateFormat | string | 'MM/dd/yyyy' | date-fns format used in the trigger label |
disabled | boolean | false | Disable interaction |
showPresets | boolean | true | Show the default Last 7/14/30/90 Days preset buttons |
presets | PresetItem[] | none | Override the default preset list |
allowFutureDates | boolean | false | When false, dates after today are disabled |
triggerClassName | string | none | Extra classes for the trigger button |
calendarClassName | string | none | Extra classes for the calendar |
modalClassName | string | none | Extra classes for the popover modal |
Any other react-day-picker props (excluding mode and selected) are forwarded to the underlying calendar.
Basic usage
const [range, setRange] = useState<DateRange>()
<DateRangePicker
selected={range}
onSelect={setRange}
/>Custom presets
const presets: PresetItem[] = [
{ label: 'This week', value: { from: startOfWeek(new Date()), to: new Date() } },
{ label: 'This month', value: { from: startOfMonth(new Date()), to: new Date() } },
]
<DateRangePicker
selected={range}
onSelect={setRange}
presets={presets}
/>Allowing future dates
<DateRangePicker
selected={range}
onSelect={setRange}
allowFutureDates
showPresets={false}
/>Styling
.mdk-date-picker__trigger: Trigger button.mdk-date-picker__modal: Popover modal container.mdk-date-picker__calendar: Calendar grid.mdk-date-picker__summary: Selected-range summary block.mdk-date-picker__presets: Preset button row
Input
Text input with label support, prefix/suffix, and search variant.
Import
import { Input } from '@mdk/core'Props
| Prop | Type | Default | Description |
|---|---|---|---|
label | string | none | Label displayed above input |
variant | 'default' | 'search' | 'default' | Input variant |
size | 'default' | 'medium' | 'default' | Input size |
error | string | none | Error message (shows red border) |
prefix | ReactNode | none | Element before input |
suffix | ReactNode | none | Element after input |
wrapperClassName | string | none | Wrapper element class |
Basic usage
<Input label="Email" placeholder="Enter email" id="email" />
<Input variant="search" placeholder="Search miners..." />With prefix/suffix
<Input prefix="$" suffix="USD" placeholder="0.00" />
<Input suffix="°C" placeholder="Temperature" />
<Input prefix={<UserIcon />} placeholder="Username" />Error state
<Input
label="MAC Address"
error="Invalid MAC address format"
value={mac}
onChange={(e) => setMac(e.target.value)}
/>Styling
.mdk-input: Input element.mdk-input__wrapper: Wrapper container.mdk-input__wrapper--error: Error state.mdk-input__label: Label element.mdk-input__prefix: Prefix element.mdk-input__suffix: Suffix element.mdk-input__error: Error message
Label
Form field label with optional required indicator.
Import
import { Label } from '@mdk/core'Basic usage
<Label htmlFor="email">Email</Label>
<Input id="email" type="email" />
<Label htmlFor="password" required>Password</Label>
<Input id="password" type="password" />Radio
Radio button for single-select from a group.
Import
import { Radio, RadioGroup } from '@mdk/core'Basic usage
<RadioGroup value={value} onValueChange={setValue}>
<Radio value="small">Small</Radio>
<Radio value="medium">Medium</Radio>
<Radio value="large">Large</Radio>
</RadioGroup>Select
Dropdown select built on Radix UI primitives.
Import
import {
Select,
SelectTrigger,
SelectValue,
SelectContent,
SelectItem,
SelectGroup,
SelectLabel,
} from '@mdk/core'SelectTrigger props
| Prop | Type | Default | Description |
|---|---|---|---|
size | 'sm' | 'md' | 'lg' | 'lg' | Trigger size |
variant | 'default' | 'colored' | 'default' | Visual variant |
color | string | none | Custom color for colored variant (hex) |
Basic usage
<Select value={value} onValueChange={setValue}>
<SelectTrigger>
<SelectValue placeholder="Select option" />
</SelectTrigger>
<SelectContent>
<SelectItem value="option1">Option 1</SelectItem>
<SelectItem value="option2">Option 2</SelectItem>
<SelectItem value="option3">Option 3</SelectItem>
</SelectContent>
</Select>With groups
<Select>
<SelectTrigger size="md">
<SelectValue placeholder="Select pool" />
</SelectTrigger>
<SelectContent>
<SelectGroup>
<SelectLabel>North America</SelectLabel>
<SelectItem value="us-east">US East</SelectItem>
<SelectItem value="us-west">US West</SelectItem>
</SelectGroup>
<SelectGroup>
<SelectLabel>Europe</SelectLabel>
<SelectItem value="eu-west">EU West</SelectItem>
</SelectGroup>
</SelectContent>
</Select>Colored variant
<Select>
<SelectTrigger variant="colored" color="#72F59E">
<SelectValue placeholder="Status" />
</SelectTrigger>
<SelectContent>
<SelectItem value="active">Active</SelectItem>
<SelectItem value="inactive">Inactive</SelectItem>
</SelectContent>
</Select>Styling
.mdk-select__trigger: Trigger button.mdk-select__trigger--{size}: Size modifier.mdk-select__trigger--colored: Colored variant.mdk-select__content: Dropdown content.mdk-select__item: Option item
Slider
Range input for numeric values.
Import
import { Slider } from '@mdk/core'Basic usage
<Slider
value={[volume]}
min={0}
max={100}
step={1}
onValueChange={([v]) => setVolume(v)}
/>Switch
Toggle switch for on/off states.
Import
import { Switch } from '@mdk/core'Props
| Prop | Type | Default | Description |
|---|---|---|---|
size | 'sm' | 'md' | 'lg' | 'md' | Switch size |
color | 'default' | 'primary' | 'success' | 'warning' | 'error' | 'default' | Color when checked |
radius | 'none' | 'small' | 'medium' | 'large' | 'full' | 'none' | Border radius |
checked | boolean | none | Checked state (controlled) |
onCheckedChange | function | none | Change callback |
Basic usage
<Switch checked={enabled} onCheckedChange={setEnabled} />
<label className="flex items-center gap-2">
<Switch checked={darkMode} onCheckedChange={setDarkMode} />
Dark Mode
</label>Sizes and colors
<Switch size="sm" />
<Switch size="md" color="primary" />
<Switch size="lg" color="success" />Styling
.mdk-switch: Root element.mdk-switch--{size}: Size modifier.mdk-switch--{color}: Color modifier.mdk-switch__thumb: Toggle thumb
TagInput
Input for entering multiple tags.
Import
import { TagInput } from '@mdk/core'Basic usage
<TagInput
value={tags}
onChange={setTags}
placeholder="Add tags..."
/>TextArea
Multi-line text input for longer content.
Import
import { TextArea } from '@mdk/core'Basic usage
<TextArea
label="Description"
placeholder="Enter description"
rows={4}
value={value}
onChange={(e) => setValue(e.target.value)}
/>Composition
The <Form> provider plus the low-level compound components for assembling custom form fields. Use these when no prebuilt field fits, or when you need full control over the field's layout.
| Component | Description |
|---|---|
Form | Form wrapper with validation and submission handling |
FormControl | Slot wrapper that wires ARIA attributes onto a control |
FormDescription | Helper text paragraph rendered below a field |
FormField | Controller wrapper that provides field context to descendants |
FormItem | Layout wrapper grouping a label, control, description, and message |
FormLabel | Label that auto-links to the form field input |
FormMessage | Validation message paragraph for a form field |
See the Composition page for <Form>, the seven compound parts, and built-in validators.
Prebuilt fields
Form-bound fields pre-wired to React hook form. Each combines FormField, FormItem, FormLabel, FormControl, FormDescription, and FormMessage so you can render a labelled, validated field from a single component.
| Component | Description |
|---|---|
FormCascader | React hook form `Cascader` wrapper for hierarchical selection |
FormCheckbox | React hook form `Checkbox` wrapper with inline label |
FormDatePicker | React hook form `DatePicker` wrapper with label and description |
FormInput | React hook form `Input` wrapper with label, description, and error |
FormRadioGroup | React hook form `RadioGroup` wrapper accepting an options array |
FormSelect | React hook form `Select` wrapper accepting an options array |
FormSwitch | React hook form `Switch` wrapper with inline label |
FormTagInput | React hook form `TagInput` wrapper for multi-value fields |
FormTextArea | React hook form `TextArea` wrapper with label, description, and error |
See the Prebuilt fields page for the full prop reference and component-specific examples.

