Skip to content
shadcn.io

shadcn.io is not affiliated with official shadcn/ui

Forgot Password Dialog Block for React Aria

A travel journal's sign-in screen with its password recovery shown open: an email field that is checked, then a Check your inbox step with the address masked, an Open your mail app link and a Resend held for 30 seconds by a timer.

Framework
UI library

Preview

Preview width

Installation

Part of Pro
Pro unlocks the code and one-line install of every Pro button and text effect, plus the MCP for your AI editor.
Have Pro already? Sign in to open it.Opens here as soon as you're Pro.

Overview

Let someone get back in without leaving the page, with this forgot password dialog for React with React Aria. It opens from the sign-in screen of Moorlight, a travel journal: the page's header holds an outline Forgot password? Button, and the sign-in card, a mark, two fields, a button and a hint line, is drawn under a soft scrim inside a hairline frame with a plus mark on each corner. The Dialog has two steps in one fixed height. The first asks for the account's email in an Input, checks its shape and sends on the one Shine Button, Send reset link, after a short wait. The second says "Check your inbox", shows the address masked as m@harrow.test, offers Open your mail app as a link, holds Resend link for 30 seconds with the catalog Timer, and lets you go back with Use a different email. Once a link has been "sent", the sign-in card behind the dialog shows "We sent a link to m@harrow.test". For preview the dialog is drawn open in place when the page loads.

FAQ

Why is the dialog already open, and what runs in it?

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. The Forgot password? button in the header opens the real Dialog, modal, with its focus trap, Escape and focus return, and the copy stays closed until a reload. Both show the same RecoverFields component and share the block's state, so an address typed in place is there in the real dialog. In your app keep the Dialog and drop the copy: the "in-place" state, the scrim and the dialog element inside the frame.

How do the two steps work?

Step one checks the address when you press Send reset link or Enter: an empty field or a malformed address says what to fix, focuses the field and clears as you correct it. A valid one waits 700 milliseconds, then step two replaces it in the same space, so the dialog never changes height, and focus lands on the sentence that names the masked address. Use a different email goes back to the field with your address still in it. Closing the dialog starts the next visit at step one, and the hint on the sign-in card stays.

How does the resend wait work?

The Timer counts down 30 seconds, and it starts only when step two appears, that is after you press Send reset link. Resend link is aria-disabled until it finishes: pressing it early says when it will work instead of doing nothing. The digits sit in one line beside the button and are not read out every second. Resend makes a new link and restarts the clock. In your app start the wait when your server sends the email and enforce it there too.

What stays local, and how do I wire it for real?

Everything: nothing is sent, which the dialog says in words, and no account is looked up. The masked address is made from what you typed, its first letter and its domain. Open your mail app is a plain mailto: link with no recipient. In your app post the address to your reset endpoint in the submit handler, answer the same way whether the account exists or not so the form can't be used to probe for accounts, and mask the address on the server.

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 email field, then moves to the confirmation when step two appears, back to the field for a different email, and Escape, Cancel and Done return it to the Forgot password? button. The button and the dialog sit inside DialogTrigger, controlled with isOpen and onOpenChange, React Aria focuses the dialog itself so a frame later focus moves to the field, and the title is a level 2 heading.

How does it look on a phone?

The copy shown on load docks to the foot of the frame as a bottom sheet with its actions pinned, and the two steps share one space so nothing jumps. The real dialog keeps the Dialog's own phone layout, a sheet from the bottom.