Key insights
- Treat paste as the primary path: when a code is pasted into any box, strip spaces and non-digits (
value.replace(/\D/g, "")) and distribute the digits across all six boxes at once. - Auto-advance focus as each digit lands, and make backspace on an empty box jump back to the previous one and clear it — so correcting a typo never traps the cursor.
- Model the field as one string, not six independent values.
useState("847291")beatsuseState(["","","","","",""]); the boxes are just a view of a single source of truth. - On mobile, wire up
inputmode="numeric"andautocomplete="one-time-code"so the OS surfaces the SMS code as a one-tap autofill above the keypad. - Throttle resend behind a visible 30s countdown. Without it, impatient users spam the button, hit
429 Too Many Requests, and get temporarily banned by the server. - Give instant feedback on submit: a wrong code shakes and clears back to focus, a correct code locks each box green with a check and a 'Verified' state.
Do / Don't
- Do: Store the full code as a single string and render the six boxes as a view of it
- Do: Strip non-digits from pasted input and spread the code across every box automatically
- Do: Gate the resend button behind a visible countdown timer to avoid rate-limit bans
- Don't: Rely on one wide input — pasted codes with spaces overflow and choke it
- Don't: Leave a wrong code sitting silently — shake, clear, and refocus instead