Skip to content

PinInput

Controlled multi-cell input for one-time codes, PINs, and short verification codes.

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 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} />
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} />
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>
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} />
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} />
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} />
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} />
Large authentication control
OTP
Invalid authentication control
OTP
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>
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} />

Controlled multi-cell input for one-time codes, PINs, and short verification codes.

When isOptional: false, isRequired: false

PropTypeDefaultDescription
classNamestring—Additional CSS class names applied to the field root, or to the input wrapper when the input is inside an InputGroup.
data-testidstring—Test ID applied to the input wrapper.
descriptionReactNode—Supporting text rendered below the label.
hasAutoFocusbooleanfalseWhether to focus the first empty cell on mount.
hasMaskbooleanfalseWhether to obscure the code's characters.
htmlNamestring—HTML name attribute for the joined hidden form input.
isDisabledbooleanfalseWhether the input is disabled.
isReadOnlybooleanfalseWhether the value is displayed without allowing focus or interaction.
isLabelHiddenbooleanfalseWhether to visually hide the label.
label*string—Field label.
labelEndReactNode—Content shown at the end of the label row, such as a link. It renders outside the label, so it does not focus the control or change its accessible name. It stays visible when isLabelHidden is true.
labelIconIconComponent—Icon shown before the label.
labelTooltipReactNode—Tooltip shown beside the label.
lengthnumber6Number 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.
refRef<HTMLDivElement>—Ref forwarded to the component's outermost element.
size"sm" | "md" | "lg"'md'Visual size.
statusInputStatus—Validation status displayed below the input.
styleCSSProperties—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.
isOptionalfalse—
isRequiredfalse—

When isOptional: true, isRequired: false

PropTypeDefaultDescription
classNamestring—Additional CSS class names applied to the field root, or to the input wrapper when the input is inside an InputGroup.
data-testidstring—Test ID applied to the input wrapper.
descriptionReactNode—Supporting text rendered below the label.
hasAutoFocusbooleanfalseWhether to focus the first empty cell on mount.
hasMaskbooleanfalseWhether to obscure the code's characters.
htmlNamestring—HTML name attribute for the joined hidden form input.
isDisabledbooleanfalseWhether the input is disabled.
isReadOnlybooleanfalseWhether the value is displayed without allowing focus or interaction.
isLabelHiddenbooleanfalseWhether to visually hide the label.
label*string—Field label.
labelEndReactNode—Content shown at the end of the label row, such as a link. It renders outside the label, so it does not focus the control or change its accessible name. It stays visible when isLabelHidden is true.
labelIconIconComponent—Icon shown before the label.
labelTooltipReactNode—Tooltip shown beside the label.
lengthnumber6Number 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.
refRef<HTMLDivElement>—Ref forwarded to the component's outermost element.
size"sm" | "md" | "lg"'md'Visual size.
statusInputStatus—Validation status displayed below the input.
styleCSSProperties—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—
isRequiredfalse—

When isOptional: false, isRequired: true

PropTypeDefaultDescription
classNamestring—Additional CSS class names applied to the field root, or to the input wrapper when the input is inside an InputGroup.
data-testidstring—Test ID applied to the input wrapper.
descriptionReactNode—Supporting text rendered below the label.
hasAutoFocusbooleanfalseWhether to focus the first empty cell on mount.
hasMaskbooleanfalseWhether to obscure the code's characters.
htmlNamestring—HTML name attribute for the joined hidden form input.
isDisabledbooleanfalseWhether the input is disabled.
isReadOnlybooleanfalseWhether the value is displayed without allowing focus or interaction.
isLabelHiddenbooleanfalseWhether to visually hide the label.
label*string—Field label.
labelEndReactNode—Content shown at the end of the label row, such as a link. It renders outside the label, so it does not focus the control or change its accessible name. It stays visible when isLabelHidden is true.
labelIconIconComponent—Icon shown before the label.
labelTooltipReactNode—Tooltip shown beside the label.
lengthnumber6Number 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.
refRef<HTMLDivElement>—Ref forwarded to the component's outermost element.
size"sm" | "md" | "lg"'md'Visual size.
statusInputStatus—Validation status displayed below the input.
styleCSSProperties—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.
isOptionalfalse—
isRequired*true—