Skip to main content

Input

  • Component overview: Standard text input for forms and single-line text entry. Supports prefix/suffix slots.
  • Size baseline: Height 40px, horizontal padding 16px, border radius 5px.
  • Implementation note: ui/input retains the input skeleton; the design layer owns the border, border radius, states, and slot layout.
  • Figma spec

Basic Usage

Result
Loading...
Live Editor
<div style={{ display: 'flex', flexDirection: 'column', gap: 16, maxWidth: 360 }}>
  <Input placeholder="Default input" />
  <Input placeholder="With initial value" defaultValue="Hello world" />
</div>

States

4 visual states: Default / Focused / Error / Disabled.

Result
Loading...
Live Editor
<div style={{ display: 'flex', flexDirection: 'column', gap: 16, maxWidth: 360 }}>
  <div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
    <span style={{ fontSize: 12, color: '#999' }}>Default</span>
    <Input placeholder="Default state" />
  </div>
  <div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
    <span style={{ fontSize: 12, color: '#999' }}>Error</span>
    <Input error placeholder="Error state" />
  </div>
  <div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
    <span style={{ fontSize: 12, color: '#999' }}>Disabled (empty)</span>
    <Input disabled placeholder="Disabled" />
  </div>
  <div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
    <span style={{ fontSize: 12, color: '#999' }}>Disabled (with value)</span>
    <Input disabled defaultValue="Read only" />
  </div>
</div>
StateBorderBackgroundNote
DefaultSeparators/Emphasized#CCCCCCtransparent
FocusedLabels/Primary#000000transparentClick to preview
ErrorStatus/Destructive#FF503Ftransparenterror prop
DisabledSeparators/Emphasized#CCCCCCGrays/Gray-1#EBEBEBdisabled prop

Main Matrix

The current spec acceptance is based on State × Filled × Status. The doc site prioritizes the core combinations matching the current implementation.

Result
Loading...
Live Editor
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(2, minmax(0, 1fr))', gap: 16, maxWidth: 760 }}>
  <div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
    <span style={{ fontSize: 12, color: '#999' }}>Normal / Empty / Default</span>
    <Input placeholder="Select content" />
  </div>
  <div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
    <span style={{ fontSize: 12, color: '#999' }}>Normal / Filled / Default</span>
    <Input defaultValue="Filled value" />
  </div>
  <div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
    <span style={{ fontSize: 12, color: '#999' }}>Focused / Empty / Default</span>
    <Input className="border-[var(--Labels-Primary)]" placeholder="Focused state" />
  </div>
  <div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
    <span style={{ fontSize: 12, color: '#999' }}>Focused / Filled / Default</span>
    <Input className="border-[var(--Labels-Primary)]" defaultValue="Focused value" />
  </div>
  <div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
    <span style={{ fontSize: 12, color: '#999' }}>Normal / Empty / Error</span>
    <Input error placeholder="Error state" />
  </div>
  <div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
    <span style={{ fontSize: 12, color: '#999' }}>Normal / Filled / Error</span>
    <Input error defaultValue="Error value" />
  </div>
  <div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
    <span style={{ fontSize: 12, color: '#999' }}>Disabled / Empty / Default</span>
    <Input disabled placeholder="Disabled empty" />
  </div>
  <div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
    <span style={{ fontSize: 12, color: '#999' }}>Disabled / Filled / Default</span>
    <Input disabled defaultValue="Disabled value" />
  </div>
</div>

Prefix and Suffix

Use the prefix and suffix props to render slot content (icons, buttons, etc.).

Result
Loading...
Live Editor
<div style={{ display: 'flex', flexDirection: 'column', gap: 16, maxWidth: 360 }}>
  <Input prefix={<Search size={16} />} placeholder="Search..." />
  <Input suffix={<Mail size={16} />} placeholder="Email" />
  <Input
    prefix={<Search size={16} />}
    suffix={<Mail size={16} />}
    placeholder="Both slots"
  />
  <Input prefix={<Search size={16} />} error placeholder="Error + prefix" />
  <Input prefix={<Search size={16} />} disabled placeholder="Disabled + prefix" />
</div>

Clearable

Set allowClear to render a clear (×) button. Following the Figma spec, the button only appears when the input is focused and has content (Filled=True + Focused); it hides on blur or when empty. Works in both controlled and uncontrolled modes.

Result
Loading...
Live Editor
function ClearableDemo() {
  const [value, setValue] = useState('Hello world')
  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 16, maxWidth: 360 }}>
      <Input allowClear defaultValue="Click to focus, then clear" />
      <Input
        allowClear
        value={value}
        onChange={(e) => setValue(e.target.value)}
        onClear={() => setValue('')}
        placeholder="Controlled clearable"
      />
    </div>
  )
}

Size Spec

DimensionValue
Height40px
Horizontal padding16px
Prefix/content/suffix gap8px
Border radius5px
FontBody/Regular (14px, line-height 22)

Props

PropTypeDefaultDescription
placeholderstring-Placeholder text
valuestring-Controlled value
defaultValuestring-Initial value (uncontrolled)
typestring'text'Input type
errorbooleanfalseError state — border uses Status/Destructive
disabledbooleanfalseDisabled state
allowClearbooleanfalseShow a clear button when focused with content
prefixReactNode-Prefix slot (e.g. icon)
suffixReactNode-Suffix slot (e.g. button)
onChangeChangeEventHandler-Change callback
onClear() => void-Clear button callback
clearButtonAriaLabelstring'清空输入'Clear button accessible name; override for localization
aria-labelstring-Accessibility label

Input.Search

  • Component overview: Search input with a search icon, clear button, and two style variants: outline and filled.
  • Figma spec
  • Implementation note: The search icon, input area, and clear button are laid out by the design layer; the underlying input structure reuses ui/input.

Variants

2 style variants × 2 sizes = 4 combinations.

Result
Loading...
Live Editor
<div style={{ display: 'flex', flexDirection: 'column', gap: 24, maxWidth: 360 }}>
  <div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
    <span style={{ fontSize: 12, color: '#999' }}>Outline / Default (40px)</span>
    <Input.Search placeholder="Search..." />
  </div>
  <div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
    <span style={{ fontSize: 12, color: '#999' }}>Outline / Compact (32px)</span>
    <Input.Search size="compact" placeholder="Search..." />
  </div>
  <div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
    <span style={{ fontSize: 12, color: '#999' }}>Filled / Default (40px)</span>
    <Input.Search variant="filled" placeholder="Search..." />
  </div>
  <div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
    <span style={{ fontSize: 12, color: '#999' }}>Filled / Compact (32px)</span>
    <Input.Search size="compact" variant="filled" placeholder="Search..." />
  </div>
</div>
StyleSizeHeightDescription
OutlineDefault40pxWith border, highlighted on focus
OutlineCompact32pxSmall with border
FilledDefault40pxGray background, no border
FilledCompact32pxSmall with gray background

Controlled + Clear

A clear button appears when the field is focused and has content (aligned with the Figma Focused + Filled state).

Result
Loading...
Live Editor
function SearchDemo() {
  const [value, setValue] = useState('')
  return (
    <div style={{ maxWidth: 360 }}>
      <Input.Search
        placeholder="Type to search..."
        value={value}
        onChange={(e) => setValue(e.target.value)}
        onClear={() => setValue('')}
      />
      {value && <p style={{ fontSize: 13, color: '#666', marginTop: 8 }}>Search: {value}</p>}
    </div>
  )
}

Props

PropTypeDefaultDescription
placeholderstring-Placeholder text
valuestring-Controlled value
defaultValuestring-Initial value (uncontrolled)
size'default' | 'compact''default'Size: default=40px, compact=32px
variant'outline' | 'filled''outline'Style: outline (with border) / filled (gray background)
onChangeChangeEventHandler-Change callback
onClear() => void-Clear button callback
clearButtonAriaLabelstring'清空搜索'Clear button accessible name; override for localization

Input.Password

  • Component overview: Password input with a built-in visibility toggle button. Click the eye icon to switch between masked and plain text.
  • Size baseline: Same as Input — height 40px, horizontal padding 16px, border radius 5px.
  • Implementation note: Composes Input with a suffix eye icon toggle. The suffix slot is occupied by the toggle button.
  • Figma spec

Basic Usage

Result
Loading...
Live Editor
<div style={{ display: 'flex', flexDirection: 'column', gap: 16, maxWidth: 360 }}>
  <Input.Password placeholder="Password" />
  <Input.Password placeholder="Password" defaultValue="Plaud-2026" />
</div>

States

Result
Loading...
Live Editor
<div style={{ display: 'flex', flexDirection: 'column', gap: 16, maxWidth: 360 }}>
  <div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
    <span style={{ fontSize: 12, color: '#999' }}>Default</span>
    <Input.Password placeholder="Password" />
  </div>
  <div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
    <span style={{ fontSize: 12, color: '#999' }}>Error</span>
    <Input.Password error placeholder="Invalid password" />
  </div>
  <div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
    <span style={{ fontSize: 12, color: '#999' }}>Disabled</span>
    <Input.Password disabled placeholder="Password" />
  </div>
  <div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
    <span style={{ fontSize: 12, color: '#999' }}>Disabled (with value)</span>
    <Input.Password disabled defaultValue="secret123" />
  </div>
</div>

Controlled + Visibility Callback

Result
Loading...
Live Editor
function PasswordDemo() {
  const [value, setValue] = useState('')
  const [visible, setVisible] = useState(false)
  return (
    <div style={{ maxWidth: 360 }}>
      <Input.Password
        placeholder="Enter password"
        value={value}
        onChange={(e) => setValue(e.target.value)}
        onVisibilityChange={setVisible}
      />
      <p style={{ fontSize: 13, color: '#666', marginTop: 8 }}>
        Visible: {visible ? 'Yes' : 'No'} | Length: {value.length}
      </p>
    </div>
  )
}

Namespace Usage

Input.Password and Input.Search are available as namespace sub-components.

Result
Loading...
Live Editor
<div style={{ display: 'flex', flexDirection: 'column', gap: 16, maxWidth: 360 }}>
  <Input.Password placeholder="Input.Password" />
  <Input.Search placeholder="Input.Search" />
</div>

Props

PropTypeDefaultDescription
placeholderstring-Placeholder text
valuestring-Controlled value
defaultValuestring-Initial value (uncontrolled)
errorbooleanfalseError state — border uses Status/Destructive
disabledbooleanfalseDisabled state
prefixReactNode-Prefix slot (e.g. lock icon)
onVisibilityChange(visible: boolean) => void-Visibility toggle callback
onChangeChangeEventHandler-Change callback
aria-labelstring-Accessibility label