TrademarkTrademarkDocs
Ctrl k
Search docs...
Form Controls

Input

Updated: September 13, 2026
View on GitHub
The Input component provides a text entry field with floating labels, validation support, and customizable styling. Use it for collecting user input in forms. The Input component supports error state and message separately. Access them with the error and errorMessage props. The Input component supports custom validation through the validate prop. This prop accepts a function that receives the input value and returns either an error message or null. The validation is happens automatically on value change with an automatic debounce of 1 second. Add icons or other elements before or after the input text. Use the placeholder prop to use placeholder instead of label. The placeholder disappears when the input has value. Use the description prop to add a description below the input. Display a character counter with color-coded feedback based on remaining characters. The counter changes color when approaching the limit. The Input component supports five sizes: extra small, small, medium, large, and extra large. iOS Safari zooms the page when a focused control computes below 16px, and it does not reliably zoom back out — the page is left scrolled somewhere else. Once UI's mobile base is 15px, which put three of the five sizes under that threshold, size="m" among them. On touch pointers only, the value is therefore clamped:
clamp
font-sizemax(token, 16px)
line-heightmax(token, 1.3 × font-size)
min-height (xs, s)40px and 48px
Line-height is clamped alongside the font because xs and s resolve --line-height-body-s to 16.88px on mobile — 16px glyphs would otherwise sit in a line box barely taller than themselves. Nothing changes on a mouse pointer, and l and xl are untouched either way: they already cleared 16px. The visible consequence is that xs, s and m render the same text size on a phone and differ by height alone, which is how iOS treats its own text fields. The floating label and the value are positioned from where their ink falls — cap height, baseline and descender — so the pair stays optically centred at whatever size and height the clamps produce. The Input component supports two variants: default and ghost. Set loading to show a spinner in the input suffix while async validation or data fetching is in progress. Fields draw no focus ring by default — the quiet, borderless look modern native inputs have settled on. Set focusRing to outline the field on focus, using the same ring Card and SmartLink use, so a keyboard user can see where they are. Worth turning on for any form someone has to tab through. Note it shows on a mouse click too: :focus-visible always matches a field that takes keyboard input, however that field was focused. Four components build on Input. They keep everything on this page and add one thing each, so they live on pages of their own:
  • NumberInput — steppers, min/max/step, and a numeric onChange
  • OTPInput — one box per digit, with paste and an onComplete
  • PasswordInput — a reveal toggle
  • TagInput — an array of removable chips instead of a string
Prop
Type
Default
idrequired
Ties the label to the field. Required, and must be unique on the page.
string—
label
Floats above the value once the field is focused or filled.
string—
placeholder
Shown while the field is empty. Not a substitute for a label.
string—
size
Field height and type scale.
xllmsxs
—
error
Put the field in its error state.
booleanfalse
errorMessage
Message shown under the field when error is set.
ReactNode—
description
Helper text under the field, for the rule the label cannot carry.
ReactNode—
corners
Round only some corners — for stacking fields into one block by hand. Inside a [Form](/form-controls/form) with density="stacked" this is worked out for you.
nonetoprightbottomlefttop-lefttop-rightbottom-rightbottom-left
—
className
string—
style
React.CSSProperties—
prefix
Rendered before the value: an icon, a currency symbol, a select.
ReactNode—
suffix
Rendered after the value: a unit, a clear button, a Kbd hint.
ReactNode—
variant
"ghost" drops the border and background, for a field inside a surface that already has them.
defaultghost
default
focusRing
booleanfalse
characterCount
Show how much has been typed, against maxLength.
boolean—
cursor
Set "interactive" for a read-only field that opens something on click.
undefinedinteractive
—
validate
Run on a debounce; return a message to show, or null to clear.
(value: ReactNode) => ReactNode | null—
loading
Show a spinner in the suffix while something resolves.
booleanfalse
span
How many columns to occupy inside a [Form](/form-controls/form) grid. Ignored outside one.
number—
…and every React.InputHTMLAttributes propinherited—
Edit this page on GitHub
TrademarkTrademark
Built with curiosity by Lorant One and the Design Engineers Club