designmotionhq

forms

OTP Input

Your OTP input is a system, not six boxes.

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") beats useState(["","","","","",""]); the boxes are just a view of a single source of truth.
  • On mobile, wire up inputmode="numeric" and autocomplete="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

Get the next pattern first

New breakdowns land in your inbox before anywhere else.

One new pattern breakdown per week. No spam, unsubscribe anytime.

Related patterns