# OTP Input

> Build a one-time-password (OTP) input with six single-digit fields that auto-advance focus as you type, support pasting a full code, and validate it against a demo code.

- Difficulty: intermediate
- Tags: useState, useRef, forms, keyboard
- Estimated time: 60 minutes
- Tests: 12
- Requires subscription: yes

## Overview

Build a one-time-password (OTP) input with six single-digit fields that auto-advance focus as the user types, support pasting a full code, and validate it against a demo code.


## Requirements

- Six single-character inputs are rendered, one per digit (`data-testid="otp-input-0"` through `otp-input-5`).
- The first input is focused on mount.
- Typing a digit fills the focused input and moves focus to the next input.
- Non-numeric characters are ignored.
- Backspace clears the current digit and stays in place.
- Backspace on an already-empty input clears the previous digit and moves focus to it.
- ArrowLeft and ArrowRight move focus between inputs.
- Pasting a code fills the inputs with the pasted digits; digits beyond six are ignored.
- The status text (`data-testid="otp-status"`) reads `Waiting for code…` while the code is incomplete.
- When all six digits are filled and match the demo code, the status reads `Code verified`.
- When all six digits are filled but do not match, the status reads `Invalid code. Try again.`
- The reset button (`data-testid="reset-otp"`) clears all inputs, refocuses the first input, and resets the status.


## Notes

- `useCopy.ts` is read-only and already wired to the "Copy code" button — the demo code is copied to the clipboard through the `useCopy` hook. Leave that file and button as they are.
- The six inputs are controlled: whatever logic you run in the change handler decides what actually sticks in state, so invalid characters never persist.
- You need to move focus between inputs imperatively — plan how to keep a reference to each field.
- A single interaction can deliver more than one digit at once (pasting a full code), not just one keypress.
- Call `preventDefault()` on the keyboard and paste handlers so the browser doesn't also move the caret or insert text on its own.
- Prefer deriving the status from the current values instead of storing it as separate state that can drift out of sync.
- `inputMode="numeric"` (and `autoComplete="one-time-code"` on the first field) improves the mobile experience.


## Tests

01. renders six inputs
02. focuses the first input on mount
03. renders the demo code
04. types a digit and moves focus to the next input
05. ignores non-numeric characters
06. backspace clears the current digit and stays in place
07. backspace on an empty input clears the previous digit
08. arrow keys move focus between inputs
09. pastes the demo code and verifies it
10. shows an error for a wrong code
11. truncates extra digits when pasting more than six
12. reset clears the inputs and refocuses the first


