Input
Generic text field with label, helper, error, prefix and suffix.
Installation
npx axiom add inputpnpm dlx axiom add inputyarn dlx axiom add inputbun x axiom add inputAlso copies field.tsx, the label and helper layout shared with TextArea, and use-input.ts, which holds the value, focus and accessibility logic.
To keep a focused field above the keyboard in a long form, put the form in a KeyboardAwareScrollView from react-native-keyboard-controller.
Usage
import { Input } from '@/components/ui/input';
<Input
label="Email"
helper="We'll never share it."
value={email}
onChangeText={setEmail}
/>
<Input label="Price" prefix="$" error="Enter a valid amount" />Props
Input accepts every TextInput prop from React Native (keyboardType, autoComplete, secureTextEntry, returnKeyType…) on top of these:
| Prop | Type | Default | Description |
|---|---|---|---|
value | string | — | Current text. Use with onChangeText for a controlled field. |
defaultValue | string | '' | Initial text when the field manages its own value. |
onChangeText | (text: string) => void | — | Called on every change. |
label | string | — | Visible label above the field. Also used as the accessibility label. |
placeholder | string | — | Hint shown while the field is empty, in content.subtle. Don’t use it as the label. |
helper | string | — | Short guidance under the field. Hidden when error is set. |
error | string | boolean | — | Puts the field in the invalid state: red border, and the message replaces helper. true changes the border only. |
prefix | ReactNode | — | Content before the text: an icon, a currency sign, a country code. |
suffix | ReactNode | — | Content after the text: a unit, a clear button, a visibility toggle. |
size | 'sm' | 'md' | 'lg' | 'md' | Minimum height from the control tokens: 32, 44 or 52pt. Grows with larger system text. |
variant | 'outline' | 'filled' | 'outline' | outline has a border, filled a subtle background with no border. |
disabled | boolean | false | Makes the field read-only with the disabled tokens. Maps to editable={false}. |
required | boolean | false | Adds a marker to the label and sets the accessibility hint. It doesn’t validate. |
onFocus / onBlur | (event) => void | — | Called when the field gains or loses focus. The focused border is handled for you. |
ref | Ref<TextInput> | — | Gives access to focus(), blur() and clear(), to chain fields on submit. |
containerStyle | StyleProp<ViewStyle> | — | Styles for the wrapper that holds label, field and helper. |
Colors come from input.{outline,filled}.{default,focused,invalid,disabled} in the component tokens. The label, helper and error are read by screen readers as the field’s label and hint, not as separate elements.
Use cases
Login form
autoComplete and keyboardType give the right keyboard and password autofill; the eye button is a suffix.
<Input label="Email" keyboardType="email-address" autoComplete="email" autoCapitalize="none" />
<Input
label="Password"
secureTextEntry={!visible}
autoComplete="current-password"
suffix={<IconButton icon="eye" size="sm" accessibilityLabel="Show password" onPress={toggle} />}
/>Validation on blur
Show the error once the user leaves the field, not while they are still typing.
<Input
label="Postcode"
value={postcode}
onChangeText={setPostcode}
onBlur={() => setTouched(true)}
error={
touched && !isPostcode(postcode)
? "Postcodes look like NW1 6XE."
: undefined
}
/>Amount with a prefix and a unit
prefix and suffix hold what the user doesn’t type. The value stays a plain number.
<Input
label="Amount"
size="lg"
keyboardType="decimal-pad"
prefix="$"
suffix="USD"
helper={`Balance: ${balance}`}
/>Chain fields with the return key
returnKeyType="next" and a ref move focus to the next field, so the user never has to reach for it.
const lastName = useRef<TextInput>(null);
<Input label="First name" returnKeyType="next" onSubmitEditing={() => lastName.current?.focus()} />
<Input ref={lastName} label="Last name" returnKeyType="done" />