Email verification looks like a small React feature until the product has to handle resend buttons, expired codes, two browser tabs, slow inboxes, and a user who clicks submit twice. At that point, a few booleans such as isLoading and isVerified stop explaining what the screen can actually do.
The fix is a small contract between the UI, the API, and the test suite. Model the meaningful states in TypeScript, make transitions explicit, and let the component render that model. The result is less clever code and fewer “why is this button stuck?” bugs.
The hidden contract in an email verification form
A verification form usually has more states than its JSX suggests:
The user has not requested a code.
A code was requested and delivery is pending.
The code can be entered.
The code is being checked.
The code is accepted, expired, or rejected.
A new code can be requested after a cooldown.
If those states are represented by independent flags, impossible combinations become easy to create.…