PinInput
OTP-style code field — raised cells with a lift-on-focus active state, auto-advance, paste / SMS-autofill distribution, and backspace transfer.
First cell carries autocomplete="one-time-code"; ←/→/Home/End move focus; Backspace transfers to the previous cell.
import { PinInput } from '@arshad-shah/cynosure-react';
export default function Example() {
return <PinInput length={6} aria-label="One-time code" />;
}
Lengths
Section titled “Lengths”Use length to size the OTP field. 4 cells for short PINs, 6 for typical OTPs.
import { PinInput } from '@arshad-shah/cynosure-react';
export default function Example() {
return (
<div style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}>
<PinInput length={4} aria-label="4-digit PIN" defaultValue="1234" />
<PinInput length={6} aria-label="6-digit OTP" defaultValue="123456" />
</div>
);
}
Three sizes are available: sm, md (default), and lg.
import { PinInput } from '@arshad-shah/cynosure-react';
export default function Example() {
return (
<div style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}>
<PinInput length={4} size="sm" aria-label="Small" defaultValue="12" />
<PinInput length={4} size="md" aria-label="Medium" defaultValue="12" />
<PinInput length={4} size="lg" aria-label="Large" defaultValue="12" />
</div>
);
}
Set type to "numeric" (default), "alphanumeric", or "alphabetic" to restrict the accepted characters.
import { PinInput } from '@arshad-shah/cynosure-react';
export default function Example() {
return (
<div style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}>
<PinInput length={4} type="numeric" aria-label="Numeric" defaultValue="1234" />
<PinInput length={4} type="alphanumeric" aria-label="Alphanumeric" defaultValue="aB7x" />
<PinInput length={4} type="alphabetic" aria-label="Alphabetic" defaultValue="abcd" />
</div>
);
}
Masked
Section titled “Masked”Set mask to render bullets in place of the entered characters, like a password field.
import { PinInput } from '@arshad-shah/cynosure-react';
export default function Example() {
return <PinInput length={6} mask aria-label="Security code" defaultValue="123456" />;
}
States
Section titled “States”import { PinInput } from '@arshad-shah/cynosure-react';
export default function Example() {
return (
<div style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}>
<PinInput length={4} aria-label="Default" defaultValue="12" />
<PinInput length={4} aria-label="Invalid" defaultValue="12" invalid />
<PinInput length={4} aria-label="Disabled" defaultValue="1234" disabled />
</div>
);
}
Accessibility
Section titled “Accessibility”- The cells are wrapped in
role="group"with anaria-label(defaults to “Verification code”). - Each cell has
aria-label="… digit N"; the first carriesautoComplete="one-time-code"so platform autofill works. - Numeric pins set
inputMode="numeric"andpattern="[0-9]*"for mobile keyboards. - Arrow Left/Right, Home/End move focus; Backspace deletes and transfers focus to the previous cell.
- Pasting a code distributes characters across the remaining cells.
Recipes
Section titled “Recipes”-
Paste a full code into any cell — it’s distributed across the cells (non-matching characters like spaces or dashes are skipped). iOS SMS one-time-code autofill is handled the same way.
-
Pass
separator(e.g."–") to visually group the cells into halves. -
Use
onCompleteto auto-submit once the user fills alllengthcells. -
Set
maskfor security codes you don’t want shoulder-surfed. -
Pair with a Resend control and a countdown timer for transactional flows.