PIN Input
A PIN input is a specialized form component that presents a series of single-character input fields for entering Personal Identification Numbers (PINs), verification codes, one-time passwords (OTPs), or similar short numeric codes. Each digit occupies its own input field, creating a segmented entry experience that clearly communicates the expected code length to the user.
The component automatically advances focus to the next field when a digit is entered, supports Backspace to move back to the previous field, and allows Arrow key navigation between fields. The combined value of all digit fields is available as a single bindable string. This pattern is commonly used in two-factor authentication flows, SMS verification, and secure PIN entry interfaces. Each individual input field has an accessible label indicating its position (e.g., "Digit 1 of 4") to support screen reader users.
Implementation Notes
- Renders as
<div role="group">containing individual single-digit<input>elements - Uses reactive state for internal digits array and two-way binding for combined value
- Auto-focuses next input on digit entry
- Backspace in empty input moves focus to previous input
- ArrowLeft/ArrowRight navigate between inputs
- Only accepts numeric characters (0-9)
inputmode="numeric"ensures numeric keyboard on mobile devicesmaxlength="1"restricts each input to a single character
Props
label: string (required) -- accessible label for the group via aria-labellength: number (default: 4) -- number of digit inputsvalue: string (default: "") -- bindable combined string of all digitsdisabled: boolean (default: false) -- disables all inputs...restProps: Any additional HTML attributes on the group div
Usage
Basic 4-digit PIN input:
<PinInput label="Enter PIN" value={pin} />
<p>Entered PIN: {pin}</p>
6-digit verification code for two-factor authentication:
<PinInput label="Verification Code" length={6} value={code} />
PIN input in a login verification form:
<Form label="Verify your identity" onsubmit={handleVerify}>
<p>Enter the 6-digit code sent to your phone.</p>
<Field label="Verification code">
<PinInput label="Verification code" length={6} value={verificationCode} />
</Field>
<Button type="submit">Verify</Button>
<ActionLink href="/resend">Resend code</ActionLink>
</Form>
Disabled state while verifying:
<PinInput label="Verifying code" length={6} value={code} disabled />
<Loading>Verifying...</Loading>
Keyboard Interactions
- Digit entry: enters value and auto-focuses next input
- Backspace: clears current or moves to previous input
- ArrowLeft: focuses previous input
- ArrowRight: focuses next input
- Tab: standard tab navigation in/out of the group
ARIA
role="group"witharia-labelon the container- Each input has
aria-label="Digit X of Y"for positional context inputmode="numeric"for numeric keyboard on mobile
When to Use
- Use for two-factor authentication, SMS verification codes, and OTP entry flows.
- Use when you need a segmented input that clearly communicates the expected code length.
- Use for MFA flows where the user enters a time-based code from an authenticator app.
- Use for account verification during sign-up or password reset.
When Not to Use
- Do not use for passwords -- use PasswordInput instead.
- Do not use for general numeric entry -- use NumberInput instead.
- Do not use for reference numbers or codes longer than 8 digits -- use TextInput with
inputmode="numeric"instead.
Headless
The PinInput headless component provides a <div role="group"> containing individual <input> fields with inputmode="numeric", maxlength="1", per-digit aria-label attributes, and auto-focus navigation. The consumer provides all visual styling for the digit fields, spacing, and container layout.
Styles
The consumer provides all CSS styling. The component renders with a .pin-input-div class for targeting. No default styles are included — this is a fully headless component.
Testing
- Verify the component renders a
<div>element with classpin-input-div - Verify role="group"
witharia-label` on the container - Verify Each input has
aria-label="Digit X of Y"for positional context - Verify keyboard interactions work correctly
- Verify pass-through attributes are applied
Advice
- Designers: Space digit fields evenly and make each field large enough for easy touch input; consider visual separators between groups (e.g., after the third digit in a 6-digit code).
- Developers: Handle paste events to distribute a pasted code across all digit fields, and auto-submit the form when all digits are entered for a smoother user experience.
Related components
password-input— an input for entering a password with obscured text <input type="password">input-with-mask— an input displaying a format mask placeholder for the user to fill in
References
- WAI-ARIA Group Role: https://www.w3.org/TR/wai-aria-1.2/#group