PinInput
Controlled multi-cell input for one-time codes, PINs, and short verification codes.
Examples
Section titled “Examples”Default
Section titled “Default”Verification code
function ControlledPinInput({ isOptional, isRequired, value: initialValue = '', ...props}: ControlledPinInputProps): React.JSX.Element { const [value, setValue] = useState(initialValue); return ( <PinInput {...props} {...getNecessity(isOptional, isRequired)} onChange={setValue} value={value} /> );}
<ControlledPinInput {...args} />Four Digit
Section titled “Four Digit”Four-digit PIN
function ControlledPinInput({ isOptional, isRequired, value: initialValue = '', ...props}: ControlledPinInputProps): React.JSX.Element { const [value, setValue] = useState(initialValue); return ( <PinInput {...props} {...getNecessity(isOptional, isRequired)} onChange={setValue} value={value} /> );}
<ControlledPinInput {...args} />Alphanumeric
Section titled “Alphanumeric”Recovery code
function ControlledPinInput({ isOptional, isRequired, value: initialValue = '', ...props}: ControlledPinInputProps): React.JSX.Element { const [value, setValue] = useState(initialValue); return ( <PinInput {...props} {...getNecessity(isOptional, isRequired)} onChange={setValue} value={value} /> );}
<ControlledPinInput {...args} />Masked
Section titled “Masked”Secret PIN
function ControlledPinInput({ isOptional, isRequired, value: initialValue = '', ...props}: ControlledPinInputProps): React.JSX.Element { const [value, setValue] = useState(initialValue); return ( <PinInput {...props} {...getNecessity(isOptional, isRequired)} onChange={setValue} value={value} /> );}
<ControlledPinInput {...args} />Small code
Medium code
Large code
function ControlledPinInput({ isOptional, isRequired, value: initialValue = '', ...props}: ControlledPinInputProps): React.JSX.Element { const [value, setValue] = useState(initialValue); return ( <PinInput {...props} {...getNecessity(isOptional, isRequired)} onChange={setValue} value={value} /> );}
<div style={{display: 'flex', flexDirection: 'column', gap: 16}}> <ControlledPinInput label="Small code" size="sm" /> <ControlledPinInput label="Medium code" size="md" /> <ControlledPinInput label="Large code" size="lg" /></div>Disabled
Section titled “Disabled”Verification code
function ControlledPinInput({ isOptional, isRequired, value: initialValue = '', ...props}: ControlledPinInputProps): React.JSX.Element { const [value, setValue] = useState(initialValue); return ( <PinInput {...props} {...getNecessity(isOptional, isRequired)} onChange={setValue} value={value} /> );}
<ControlledPinInput {...args} />Required
Section titled “Required”Verification codeRequired
function ControlledPinInput({ isOptional, isRequired, value: initialValue = '', ...props}: ControlledPinInputProps): React.JSX.Element { const [value, setValue] = useState(initialValue); return ( <PinInput {...props} {...getNecessity(isOptional, isRequired)} onChange={setValue} value={value} /> );}
<ControlledPinInput {...args} />With Description
Section titled “With Description”Verification codeEnter the code sent to your phone.
function ControlledPinInput({ isOptional, isRequired, value: initialValue = '', ...props}: ControlledPinInputProps): React.JSX.Element { const [value, setValue] = useState(initialValue); return ( <PinInput {...props} {...getNecessity(isOptional, isRequired)} onChange={setValue} value={value} /> );}
<ControlledPinInput {...args} />Error Status
Section titled “Error Status”Verification code
That code has expired.
function ControlledPinInput({ isOptional, isRequired, value: initialValue = '', ...props}: ControlledPinInputProps): React.JSX.Element { const [value, setValue] = useState(initialValue); return ( <PinInput {...props} {...getNecessity(isOptional, isRequired)} onChange={setValue} value={value} /> );}
<ControlledPinInput {...args} />In Input Group
Section titled “In Input Group”Large authentication control
OTP
Invalid authentication control
OTP
Check the supplied code.
Disabled authentication control
OTP
function ControlledPinInput({ isOptional, isRequired, value: initialValue = '', ...props}: ControlledPinInputProps): React.JSX.Element { const [value, setValue] = useState(initialValue); return ( <PinInput {...props} {...getNecessity(isOptional, isRequired)} onChange={setValue} value={value} /> );}
<div style={{display: 'flex', flexDirection: 'column', gap: 16}}> <InputGroup label="Large authentication control" size="lg"> <InputGroupText>OTP</InputGroupText> <ControlledPinInput label="Verification code" size="sm" /> </InputGroup> <InputGroup label="Invalid authentication control" status={{message: 'Check the supplied code.', type: 'error'}}> <InputGroupText>OTP</InputGroupText> <ControlledPinInput label="Verification code" /> </InputGroup> <InputGroup isDisabled label="Disabled authentication control"> <InputGroupText>OTP</InputGroupText> <ControlledPinInput label="Verification code" /> </InputGroup></div>Read Only
Section titled “Read Only”Verification code
function ControlledPinInput({ isOptional, isRequired, value: initialValue = '', ...props}: ControlledPinInputProps): React.JSX.Element { const [value, setValue] = useState(initialValue); return ( <PinInput {...props} {...getNecessity(isOptional, isRequired)} onChange={setValue} value={value} /> );}
<ControlledPinInput {...args} />PinInput
Section titled “PinInput”Controlled multi-cell input for one-time codes, PINs, and short verification codes.
When isOptional: false, isRequired: false
| Prop | Type | Default | Description |
|---|---|---|---|
className | string | — | Additional CSS class names applied to the field root, or to the input wrapper when the input is inside an InputGroup. |
data-testid | string | — | Test ID applied to the input wrapper. |
description | ReactNode | — | Supporting text rendered below the label. |
hasAutoFocus | boolean | false | Whether to focus the first empty cell on mount. |
hasMask | boolean | false | Whether to obscure the code's characters. |
htmlName | string | — | HTML name attribute for the joined hidden form input. |
isDisabled | boolean | false | Whether the input is disabled. |
isReadOnly | boolean | false | Whether the value is displayed without allowing focus or interaction. |
isLabelHidden | boolean | false | Whether to visually hide the label. |
label* | string | — | Field label. |
labelIcon | IconComponent | — | Icon shown before the label. |
labelTooltip | ReactNode | — | Tooltip shown beside the label. |
length | number | 6 | Number of code cells. |
onBlur | (event: FocusEvent<HTMLInputElement>) => void | — | Called when focus leaves the input's cells. Moving between cells does not trigger it. |
onChange* | ( value: string, event: ChangeEvent<HTMLInputElement> | null, ) => void | — | Called with the next joined value. |
onComplete | (value: string) => void | — | Called when an edit transitions the code from incomplete to complete. |
onFocus | (event: FocusEvent<HTMLInputElement>) => void | — | Called when focus enters the input's cells. Moving between cells does not trigger it. |
ref | Ref<HTMLDivElement> | — | Ref forwarded to the component's outermost element. |
size | "sm" | "md" | "lg" | 'md' | Visual size. |
status | InputStatus | — | Validation status displayed below the input. |
style | CSSProperties | — | Inline styles applied to the field root, or to the input wrapper when the input is inside an InputGroup. |
type | "numeric" | "alphanumeric" | 'numeric' | Characters accepted from user input. |
value* | string | — | Controlled joined code value. Characters outside the set accepted by type are ignored. |
isOptional | false | — | |
isRequired | false | — |
When isOptional: true, isRequired: false
| Prop | Type | Default | Description |
|---|---|---|---|
className | string | — | Additional CSS class names applied to the field root, or to the input wrapper when the input is inside an InputGroup. |
data-testid | string | — | Test ID applied to the input wrapper. |
description | ReactNode | — | Supporting text rendered below the label. |
hasAutoFocus | boolean | false | Whether to focus the first empty cell on mount. |
hasMask | boolean | false | Whether to obscure the code's characters. |
htmlName | string | — | HTML name attribute for the joined hidden form input. |
isDisabled | boolean | false | Whether the input is disabled. |
isReadOnly | boolean | false | Whether the value is displayed without allowing focus or interaction. |
isLabelHidden | boolean | false | Whether to visually hide the label. |
label* | string | — | Field label. |
labelIcon | IconComponent | — | Icon shown before the label. |
labelTooltip | ReactNode | — | Tooltip shown beside the label. |
length | number | 6 | Number of code cells. |
onBlur | (event: FocusEvent<HTMLInputElement>) => void | — | Called when focus leaves the input's cells. Moving between cells does not trigger it. |
onChange* | ( value: string, event: ChangeEvent<HTMLInputElement> | null, ) => void | — | Called with the next joined value. |
onComplete | (value: string) => void | — | Called when an edit transitions the code from incomplete to complete. |
onFocus | (event: FocusEvent<HTMLInputElement>) => void | — | Called when focus enters the input's cells. Moving between cells does not trigger it. |
ref | Ref<HTMLDivElement> | — | Ref forwarded to the component's outermost element. |
size | "sm" | "md" | "lg" | 'md' | Visual size. |
status | InputStatus | — | Validation status displayed below the input. |
style | CSSProperties | — | Inline styles applied to the field root, or to the input wrapper when the input is inside an InputGroup. |
type | "numeric" | "alphanumeric" | 'numeric' | Characters accepted from user input. |
value* | string | — | Controlled joined code value. Characters outside the set accepted by type are ignored. |
isOptional* | true | — | |
isRequired | false | — |
When isOptional: false, isRequired: true
| Prop | Type | Default | Description |
|---|---|---|---|
className | string | — | Additional CSS class names applied to the field root, or to the input wrapper when the input is inside an InputGroup. |
data-testid | string | — | Test ID applied to the input wrapper. |
description | ReactNode | — | Supporting text rendered below the label. |
hasAutoFocus | boolean | false | Whether to focus the first empty cell on mount. |
hasMask | boolean | false | Whether to obscure the code's characters. |
htmlName | string | — | HTML name attribute for the joined hidden form input. |
isDisabled | boolean | false | Whether the input is disabled. |
isReadOnly | boolean | false | Whether the value is displayed without allowing focus or interaction. |
isLabelHidden | boolean | false | Whether to visually hide the label. |
label* | string | — | Field label. |
labelIcon | IconComponent | — | Icon shown before the label. |
labelTooltip | ReactNode | — | Tooltip shown beside the label. |
length | number | 6 | Number of code cells. |
onBlur | (event: FocusEvent<HTMLInputElement>) => void | — | Called when focus leaves the input's cells. Moving between cells does not trigger it. |
onChange* | ( value: string, event: ChangeEvent<HTMLInputElement> | null, ) => void | — | Called with the next joined value. |
onComplete | (value: string) => void | — | Called when an edit transitions the code from incomplete to complete. |
onFocus | (event: FocusEvent<HTMLInputElement>) => void | — | Called when focus enters the input's cells. Moving between cells does not trigger it. |
ref | Ref<HTMLDivElement> | — | Ref forwarded to the component's outermost element. |
size | "sm" | "md" | "lg" | 'md' | Visual size. |
status | InputStatus | — | Validation status displayed below the input. |
style | CSSProperties | — | Inline styles applied to the field root, or to the input wrapper when the input is inside an InputGroup. |
type | "numeric" | "alphanumeric" | 'numeric' | Characters accepted from user input. |
value* | string | — | Controlled joined code value. Characters outside the set accepted by type are ignored. |
isOptional | false | — | |
isRequired* | true | — |