MDK Logo

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

Controls

Standalone form controls. Manage value and onChange yourself, or compose them inside <Form> with FormField.

ComponentDescription
ButtonPrimary action trigger with variants and sizes
ActionButtonButton with loading state and icon support
InputText input field with label and validation
TextAreaMulti-line text input
SelectDropdown single/multi-select input
CascaderMulti-level dropdown selector for hierarchical data
CheckboxBinary toggle input for forms
SwitchToggle for on/off settings
RadioSingle-select option from a group
DatePickerCalendar-based date selection input
DateRangePickerTwo-month date range selector with presets and apply/clear actions
SliderRange input for numeric values
LabelForm field label with optional required indicator
TagInputInput 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

PropTypeDefaultDescription
variant'primary' | 'secondary' | 'danger' | 'tertiary' | 'link' | 'icon' | 'outline' | 'ghost''secondary'Visual variant
size'sm' | 'md' | 'lg'noneButton size
loadingbooleanfalseShow loading spinner
fullWidthbooleanfalseExpand to container width
iconReactNodenoneIcon element
iconPosition'left' | 'right''left'Icon placement
disabledbooleanfalseDisable 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

PropTypeDefaultDescription
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
checkedboolean | 'indeterminate'noneChecked state (controlled)
onCheckedChangefunctionnoneChange 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

PropTypeDefaultDescription
selectedDateRangenoneSelected { from, to } range (controlled)
onSelect(range: DateRange | undefined) => voidnoneCalled when the user applies a range
placeholderstring'Pick a date range'Trigger text when no range is selected
dateFormatstring'MM/dd/yyyy'date-fns format used in the trigger label
disabledbooleanfalseDisable interaction
showPresetsbooleantrueShow the default Last 7/14/30/90 Days preset buttons
presetsPresetItem[]noneOverride the default preset list
allowFutureDatesbooleanfalseWhen false, dates after today are disabled
triggerClassNamestringnoneExtra classes for the trigger button
calendarClassNamestringnoneExtra classes for the calendar
modalClassNamestringnoneExtra 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

PropTypeDefaultDescription
labelstringnoneLabel displayed above input
variant'default' | 'search''default'Input variant
size'default' | 'medium''default'Input size
errorstringnoneError message (shows red border)
prefixReactNodenoneElement before input
suffixReactNodenoneElement after input
wrapperClassNamestringnoneWrapper 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

PropTypeDefaultDescription
size'sm' | 'md' | 'lg''lg'Trigger size
variant'default' | 'colored''default'Visual variant
colorstringnoneCustom 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

PropTypeDefaultDescription
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
checkedbooleannoneChecked state (controlled)
onCheckedChangefunctionnoneChange 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.

ComponentDescription
FormForm wrapper with validation and submission handling
FormControlSlot wrapper that wires ARIA attributes onto a control
FormDescriptionHelper text paragraph rendered below a field
FormFieldController wrapper that provides field context to descendants
FormItemLayout wrapper grouping a label, control, description, and message
FormLabelLabel that auto-links to the form field input
FormMessageValidation 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.

ComponentDescription
FormCascaderReact hook form `Cascader` wrapper for hierarchical selection
FormCheckboxReact hook form `Checkbox` wrapper with inline label
FormDatePickerReact hook form `DatePicker` wrapper with label and description
FormInputReact hook form `Input` wrapper with label, description, and error
FormRadioGroupReact hook form `RadioGroup` wrapper accepting an options array
FormSelectReact hook form `Select` wrapper accepting an options array
FormSwitchReact hook form `Switch` wrapper with inline label
FormTagInputReact hook form `TagInput` wrapper for multi-value fields
FormTextAreaReact hook form `TextArea` wrapper with label, description, and error

See the Prebuilt fields page for the full prop reference and component-specific examples.

On this page