shadcn.io is not affiliated with official shadcn/ui
Email Verification Dialog Block for React
A newsletter tool's profile page with its email verification shown open: a six-slot code that checks itself when full, three tries, a resend held for 30 seconds by a timer, a way to use a different address, and a success state.
Installation
Overview
Confirm that an address is really theirs, with this verification dialog for React with Base UI. It opens from the Profile page of Postpetal, a newsletter tool: the header holds an outline Verify email Button, and the profile itself, a cover band, an avatar, the name, the address with an Unverified badge and three stats, is drawn under a soft scrim inside a hairline frame with a plus mark on each corner. The Dialog holds a six-slot Input OTP: type the digits or paste the whole code, and it checks itself when it is full. A wrong code marks the slots and says how many tries remain; after three the slots lock and say to request a new code. Resend code is held for 30 seconds by a Timer that counts in the line beside it, and Use a different email swaps in an address field. The demo code is shown, since nothing is sent. The one Shine Button is Verify code, then Send code or Done as the step changes. A right code shows a result and flips the drawn badge to Verified. For preview the dialog is drawn open in place when the page loads; the header button opens the real Dialog, with the same code and the same attempts. For sign-up, a changed address, a newsletter's sender and any account that needs a confirmed email.
FAQ
Why is the dialog already open, and why is the clock stopped?
So you can see it without a click. On load the dialog is a copy drawn in place over the page: a native dialog element that isn't modal, so nothing is trapped, hidden or focused, and the page around the preview keeps working. A countdown would age the preview, so in the copy the Timer is stopped at 30 seconds. The Verify email button opens the real Dialog, modal, with its focus trap, Escape and focus return, and there the clock runs from open. The copy stays closed until a reload. Both show the same VerifyFields component and share the block's state, so digits typed in the copy are there in the real one. In your app keep the Dialog and drop the copy: the "in-place" state, the scrim and the dialog element inside the frame.
How does the code work?
CODES holds the codes this demo accepts, one per request, and the dialog shows the current one under the slots, since nothing is emailed. Digits go in one at a time or as a paste; anything that isn't a digit is dropped, and when all six are in the code is checked at once, with no button to press. A wrong code keeps the digits on screen and marks the slots with their border and a message: "That code doesn't match. Tries remaining: 2." Editing clears it. After ATTEMPTS misses the slots lock and the message asks for a new code. The value is one string with a maximum length of six.
How does the resend wait work?
The Timer counts down COOLDOWN, 30 seconds, and Resend code is aria-disabled until it finishes: pressing it early says when it will work instead of doing nothing. The digits are in one line beside the button and are not read out every second; a polite message says when you can resend. Resend makes the next code, clears the slots, gives back the three attempts and restarts the clock. Under reduced motion the digits still change, since they are the information. In your app start the wait when your server sends the code and keep the count in a place the server can check too.
What does verifying do here, and how do I wire it up?
Here the code is checked against CODES, a right one shows the result and flips the drawn badge to Verified, Done closes the dialog and the header's button becomes Email verified, and a polite status message says nothing was sent. Use a different email checks the shape of the address, makes a new code and restarts the clock. In your app have your server send the code by email, check it there in check, count the attempts there too, and keep the dialog open with a busy state until it answers.
Where does focus go?
The real dialog is named by its title, which follows the step, and described by its sentence. Focus starts in the first slot, then moves where the step needs it: to the address field for a different email, back to the first slot for the code, and to the result when the code is right. Escape, Cancel and the close button return focus to the Verify email button. The trigger is passed to DialogTrigger through render, and the dialog is controlled with open and onOpenChange. A verified address has nothing left to verify, so pressing the button then says so and the dialog stays shut.
Related
- Pro
PIN Keypad Dialog
Account and securityA mobile wallet's transfer review with its PIN dialog shown open: four masked slots over a keypad of twelve keys, typed digits and keypad presses writing the same value, and a fourth digit that approves.
- Pro
Recovery Options Account
Sign-in methodsAn account's recovery options: four numbered ways back in, each with its state in words, a masked value, a quiet test and a one-field form to set it up, and a line that counts how many are ready.
- Pro
OTP Verification Login
Codes and approvalsA code step for a parcel locker app: a six-digit field that checks itself on the sixth digit or on a paste, counts wrong codes down from three, and a resend button that waits 30 seconds after it is pressed.
- Pro
Change Password Dialog
Account and securityA password manager's account page with its change-password dialog shown open: three password fields, a live checklist of five rules with a strength meter, a match line, and an Update button that stays quiet until every rule passes.
- Pro
Session Timeout Dialog
Status and errorsA bank's accounts page with an alert dialog shown open that warns before an idle session ends: a one-minute clock with a draining bar, Stay signed in and Sign out now, and a signed-out view at zero.
- Pro
Add Profile Dialog
Account and securityA newsletter's writers page with its byline composer shown open: a name, what they write, a 120 character bio, a mark from six drawn glyphs or initials and a switch, beside a card that shows the byline as it will print.