Input Components

April 6, 2026 · View on GitHub

User input controls, forms, and selection widgets.

Input

TextInput

Single-line text input with cursor movement, undo/redo (Ctrl+Z/Y), command history (Up/Down arrows), and paste support.

PropTypeDefaultDescription
valuestring--Current input value (required, controlled)
onChange(value: string) => void--Called on every keystroke (required)
onSubmit(value: string) => void--Called on Enter key
placeholderstring--Placeholder text when empty
focusbooleantrueWhether input captures keyboard
colorstring | number--Text color
placeholderColorstring | number--Placeholder text color
historystring[][]Previous inputs for Up/Down navigation
widthnumber | \${number}%``--Input width
heightnumber1Input height
flexnumber--Flex grow
aria-labelstring--Accessibility label

Basic: Simple input

import { TextInput } from "@orchetron/storm";
import { useState } from "react";

function NameInput() {
  const [name, setName] = useState("");
  return (
    <TextInput
      value={name}
      onChange={setName}
      onSubmit={(v) => console.log("Name:", v)}
      placeholder="Enter your name..."
    />
  );
}

Advanced: Command prompt with history

function CommandPrompt({ history, onCommand }: { history: string[]; onCommand: (cmd: string) => void }) {
  const [value, setValue] = useState("");

  return (
    <Box flexDirection="row" gap={1}>
      <Text color="#82AAFF" bold>{">"}</Text>
      <TextInput
        value={value}
        onChange={setValue}
        onSubmit={(cmd) => {
          onCommand(cmd);
          setValue("");
        }}
        placeholder="Type a command..."
        history={history}
        color="#D4D4D4"
        placeholderColor="#505050"
        flex={1}
      />
    </Box>
  );
}

Note: For multi-line text input, use ChatInput which supports multi-line editing with Enter for newlines and configurable submit behavior.


ChatInput

Auto-wrapping, auto-expanding chat prompt input. Grows from 1 row to maxRows, then scrolls. Supports undo/redo, selection, history, and multiline mode.

PropTypeDefaultDescription
valuestring--Current input value
onChange(value: string) => void--Value change callback
onSubmit(value: string) => void--Submit callback (Enter key)
placeholderstring--Placeholder text
maxRowsnumber4Max rows before scrolling
maxLengthnumber--Maximum character count
focusbooleantrueWhether input is focused
colorstring | number--Text color
historystring[][]Command history (up/down arrows)
multilinebooleanfalseEnter inserts newline; Ctrl+Enter sends
disabledbooleanfalseNon-interactive mode
promptCharstringpersonality defaultOverride prompt character
cursorStyle"block" | "underline" | "bar"personality defaultCursor display style
<ChatInput value={text} onChange={setText} onSubmit={send} placeholder="Type a message..." />

Button

Pressable button rendered as [ Label ]. Enter or Space triggers the onPress callback. Supports focus state, disabled state, and a loading spinner.

PropTypeDefaultDescription
labelstring--Button label text (required)
onPress() => void--Called on Enter/Space
isFocusedbooleantrueWhether button shows focused style and accepts input
disabledbooleanfalseDisable interaction and dim the label
loadingbooleanfalseShow spinner animation beside label
colorstring | numbercolors.brand.primaryButton color
boldboolean--Override bold style
dimboolean--Override dim style
aria-labelstring--Accessibility label
Plus layout propswidth, height, margin*, minWidth, maxWidth

Basic: Submit button

import { Button } from "@orchetron/storm";

<Button label="Submit" onPress={() => handleSubmit()} />

Advanced: Button row with states

<Box flexDirection="row" gap={2}>
  <Button
    label="Save"
    onPress={handleSave}
    isFocused={activeButton === "save"}
    loading={isSaving}
  />
  <Button
    label="Cancel"
    onPress={handleCancel}
    isFocused={activeButton === "cancel"}
    color="#F87171"
  />
  <Button
    label="Delete"
    onPress={handleDelete}
    disabled={!canDelete}
    isFocused={activeButton === "delete"}
  />
</Box>

Checkbox

Toggleable checkbox rendered as [✓] or [ ] with an optional label. Space or Enter toggles the checked state.

PropTypeDefaultDescription
checkedboolean--Whether checked (required, controlled)
onChange(checked: boolean) => void--Called on toggle
labelstring--Label text shown after checkbox
disabledbooleanfalseDisable interaction
colorstring | numbercolors.brand.primaryCheck mark color
boldboolean--Override bold
dimboolean--Override dim
aria-labelstring--Accessibility label
Plus layout propswidth, height, margin*, minWidth, maxWidth

Basic: Single checkbox

import { Checkbox } from "@orchetron/storm";

<Checkbox checked={agreed} onChange={setAgreed} label="I agree to the terms" />

Advanced: Feature toggles

<Box flexDirection="column" gap={0}>
  <Checkbox checked={features.logging} onChange={(v) => setFeature("logging", v)} label="Enable logging" />
  <Checkbox checked={features.metrics} onChange={(v) => setFeature("metrics", v)} label="Collect metrics" />
  <Checkbox checked={features.debug} onChange={(v) => setFeature("debug", v)} label="Debug mode" disabled={isProduction} />
</Box>

Switch

On/off toggle switch with a visual track indicator. Space or Enter toggles the state. Shows ON/OFF labels by default.

PropTypeDefaultDescription
checkedboolean--Whether switch is on (required, controlled)
onChange(checked: boolean) => void--Called on toggle
labelstring--Label text shown after status
onLabelstring"ON"Text when checked
offLabelstring"OFF"Text when unchecked
isFocusedbooleantrueWhether switch captures input
colorstring | numbercolors.successActive color
boldboolean--Override bold
dimboolean--Override dim
aria-labelstring--Accessibility label
Plus layout propswidth, height, margin*, minWidth, maxWidth

Basic: Simple toggle

import { Switch } from "@orchetron/storm";

<Switch checked={darkMode} onChange={setDarkMode} label="Dark mode" />

Advanced: Custom labels with multiple switches

<Box flexDirection="column" gap={1}>
  <Switch checked={autoSave} onChange={setAutoSave} label="Auto-save" onLabel="Enabled" offLabel="Disabled" />
  <Switch checked={notifications} onChange={setNotifications} label="Notifications" color="#82AAFF" />
  <Switch checked={experimental} onChange={setExperimental} label="Experimental features" isFocused={false} />
</Box>

RadioGroup

Single-selection radio button list. Renders filled/empty circle indicators. Up/Down arrows navigate, Enter/Space selects.

PropTypeDefaultDescription
optionsreadonly RadioOption[]--Array of { value, label } (required)
valuestring--Currently selected value (required, controlled)
onChange(value: string) => void--Called on selection
direction"column" | "row""column"Layout direction
isFocusedbooleantrueWhether group captures input
colorstring | numbercolors.brand.primarySelected indicator color
aria-labelstring--Accessibility label
Plus layout propswidth, height, margin*, minWidth, maxWidth

Basic: Vertical radio group

import { RadioGroup } from "@orchetron/storm";

<RadioGroup
  options={[
    { value: "small", label: "Small" },
    { value: "medium", label: "Medium" },
    { value: "large", label: "Large" },
  ]}
  value={size}
  onChange={setSize}
/>

Advanced: Horizontal layout with custom color

<Box flexDirection="column" gap={1}>
  <Text bold>Select region:</Text>
  <RadioGroup
    options={[
      { value: "us-east", label: "US East" },
      { value: "us-west", label: "US West" },
      { value: "eu-west", label: "EU West" },
      { value: "ap-south", label: "Asia Pacific" },
    ]}
    value={region}
    onChange={setRegion}
    direction="row"
    color="#82AAFF"
  />
</Box>

Select

Dropdown select with inline search filtering. When closed, shows selected label. When open, renders a bordered dropdown navigable with Up/Down/Enter/Escape. Type to filter.

PropTypeDefaultDescription
optionsArray<{ label: string; value: string }>--Selectable options (required)
valuestring--Currently selected value
onChange(value: string) => void--Called on selection
placeholderstring"Select..."Placeholder when nothing selected
isOpenbooleanfalseWhether dropdown is open (controlled)
onOpenChange(open: boolean) => void--Called when dropdown opens/closes
isFocusedbooleantrueWhether select captures input
colorstring | numbercolors.brand.primaryAccent color
aria-labelstring--Accessibility label
Plus layout propswidth, height, margin*, minWidth, maxWidth

Basic: Simple dropdown

import { Select } from "@orchetron/storm";

<Select
  options={[
    { label: "Node.js", value: "node" },
    { label: "Python", value: "python" },
    { label: "Rust", value: "rust" },
  ]}
  value={language}
  onChange={setLanguage}
  isOpen={isOpen}
  onOpenChange={setIsOpen}
/>

Advanced: Controlled dropdown with label

function LanguagePicker() {
  const [lang, setLang] = useState("node");
  const [open, setOpen] = useState(false);

  return (
    <Box flexDirection="column">
      <Text bold marginBottom={1}>Runtime:</Text>
      <Select
        options={[
          { label: "Node.js 20 LTS", value: "node20" },
          { label: "Node.js 22 Current", value: "node22" },
          { label: "Deno 2.0", value: "deno2" },
          { label: "Bun 1.1", value: "bun" },
        ]}
        value={lang}
        onChange={(v) => { setLang(v); setOpen(false); }}
        isOpen={open}
        onOpenChange={setOpen}
        placeholder="Choose runtime..."
        color="#82AAFF"
        width={30}
      />
    </Box>
  );
}

SearchInput

Text input with a magnifying glass icon prefix. Wraps TextInput with search-oriented defaults.

PropTypeDefaultDescription
valuestring--Current search value (required, controlled)
onChange(value: string) => void--Called on every keystroke (required)
onSubmit(value: string) => void--Called on Enter
placeholderstring"Search..."Placeholder text
focusbooleantrueWhether input captures keyboard
colorstring | number--Text color
aria-labelstring--Accessibility label
Plus layout propswidth, height, margin*, minWidth, maxWidth

Basic: Search field

import { SearchInput } from "@orchetron/storm";

<SearchInput value={query} onChange={setQuery} onSubmit={runSearch} />

Advanced: Search with results count

<Box flexDirection="column" gap={1}>
  <SearchInput
    value={query}
    onChange={setQuery}
    placeholder="Filter components..."
    width={40}
  />
  <Text dim>{filteredItems.length} results</Text>
</Box>

Form

Multi-field form container with Tab/Enter navigation, built-in validation, and a submit button. Supports text, password, and number field types.

PropTypeDefaultDescription
fieldsFormField[]--Array of field definitions (required)
onSubmit(values: Record<string, string>) => void--Called with all field values on submit
isFocusedbooleantrueWhether form captures input
submitLabelstring"Submit"Label for the submit button
colorstring | numbercolors.brand.primaryAccent color
aria-labelstring--Accessibility label
Plus container propspadding*, borderStyle, borderColor, backgroundColor, width, margin*

FormField type:

PropertyTypeDefaultDescription
keystring--Unique field identifier
labelstring--Display label
type"text" | "password" | "number""text"Input type
placeholderstring--Placeholder text
requiredboolean--Marks field as required
validate(value: string) => string | null--Custom validation returning error or null
patternRegExp--Regex pattern validation
minLengthnumber--Minimum input length
maxLengthnumber--Maximum input length

Basic: Login form

import { Form } from "@orchetron/storm";

<Form
  fields={[
    { key: "username", label: "Username", required: true },
    { key: "password", label: "Password", type: "password", required: true },
  ]}
  onSubmit={(values) => login(values.username, values.password)}
/>

Advanced: Validated registration form

<Form
  fields={[
    { key: "email", label: "Email", required: true, pattern: /^[^@]+@[^@]+\.[^@]+$/ },
    { key: "password", label: "Password", type: "password", required: true, minLength: 8 },
    { key: "port", label: "Port", type: "number", placeholder: "8080" },
    {
      key: "name",
      label: "Display Name",
      maxLength: 32,
      validate: (v) => v.includes(" ") ? null : "Must include first and last name",
    },
  ]}
  onSubmit={handleRegistration}
  submitLabel="Create Account"
  borderStyle="round"
  borderColor="#82AAFF"
  padding={1}
/>

MaskedInput

Formatted text input with a mask pattern where # = digit, A = letter, * = any character. Literal characters in the mask are auto-advanced.

PropTypeDefaultDescription
valuestring--Current raw input value
onChange(value: string) => void--Called when value changes
onSubmit(value: string) => void--Called on Enter
maskstring--Mask pattern (#=digit, A=letter, *=any)
placeholderstring--Placeholder text when empty
colorstring | numbercolors.text.primaryText color
focusbooleantrueWhether the input is focused
disabledbooleanfalseDisable input
widthnumber | \${number}%``--Explicit width
heightnumber | \${number}%``--Explicit height
flexnumber--Flex grow shorthand
renderDisplay(formatted: string, cursor: number) => ReactNode--Custom display renderer
aria-labelstring--Accessibility label
const [phone, setPhone] = useState("");
<MaskedInput value={phone} onChange={setPhone} mask="(###) ###-####" placeholder="Phone" />

FilePicker

File/directory tree navigator with fuzzy type-to-search, extension filtering, and file metadata display.

PropTypeDefaultDescription
filesFileNode[]--File tree to display
onSelect(path: string) => void--File selection callback
selectedPathstring--Currently selected path
maxVisiblenumber--Max visible entries
isFocusedboolean--Enable keyboard navigation
colorstring | number--Text color
extensionsstring[]--Filter by extensions (e.g. [".ts"])
showSizeboolean--Show file sizes
showModifiedboolean--Show last modified date
renderEntry(file, state) => ReactNode--Custom entry renderer
<FilePicker files={fileTree} onSelect={openFile} extensions={[".ts", ".tsx"]} isFocused />

SelectInput

Arrow-key navigable single-select list with type-to-filter.

PropTypeDefaultDescription
itemsSelectInputItem[]--Items ({ label, value })
onSelect(item: SelectInputItem) => void--Called on Enter
onHighlight(item: SelectInputItem) => void--Called when highlight changes
initialIndexnumber0Initially highlighted index
isFocusedbooleantrueAccept keyboard input
maxVisiblenumber--Max visible items before scrolling
renderItem(item, state) => ReactNode--Custom item renderer
aria-labelstring--Accessibility label
<SelectInput
  items={[
    { label: "TypeScript", value: "ts" },
    { label: "Rust", value: "rs" },
    { label: "Go", value: "go" },
  ]}
  onSelect={(item) => console.log(item.value)}
/>

SelectionList

Multi-select checklist with keyboard navigation, range selection (Shift+Up/Down), and type-to-filter.

PropTypeDefaultDescription
itemsArray<{ label, value }>--Selectable items
selectedValuesstring[]--Currently selected values
onChange(values: string[]) => void--Called when selection changes
checkColorstring | numbercolors.successCheckbox color
highlightColorstring | numbercolors.brand.primaryHighlighted item color
isFocusedbooleantrueAccept keyboard input
renderItem(item, state) => ReactNode--Custom item renderer
aria-labelstring--Accessibility label

Keys: Space=toggle, A=select all, N=deselect all, Shift+Up/Down=range select.

<SelectionList
  items={[{ label: "Apple", value: "apple" }, { label: "Banana", value: "banana" }]}
  selectedValues={selected}
  onChange={setSelected}
/>

OptionList

Scrollable list of options with keyboard navigation and type-to-filter. Lightweight alternative to Select for flat option lists.

PropTypeDefaultDescription
optionsArray<{ label: string; value: string }>--Options to display
onSelect(value: string) => void--Called on Enter
isFocusedbooleantrueAccept keyboard input
<OptionList options={[{ label: "Yes", value: "y" }, { label: "No", value: "n" }]} onSelect={handle} />

TextArea

Multi-line text editor with line numbers, word wrap, and scroll support.

PropTypeDefaultDescription
valuestring--Current text content
onChange(value: string) => void--Called on change
heightnumber10Visible rows
placeholderstring--Placeholder text
<TextArea value={text} onChange={setText} height={8} placeholder="Enter description..." />

DatePicker

Calendar-based date selector with keyboard navigation. Built on useCalendarBehavior.

PropTypeDefaultDescription
valueDate--Selected date
onChange(date: Date) => void--Called on selection
isFocusedbooleantrueAccept keyboard input
<DatePicker value={date} onChange={setDate} />

Back to Components