Skip to content
shadcn.io

shadcn.io is not affiliated with official shadcn/ui

Two-Step Setup Dialog Block for React

A cloud editor's security page with a three-step dialog shown open: a QR code and its key, eight backup codes to copy or download, and a six-digit check, ending in a note that two-step sign-in is on.

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

Walk people through adding a second step to signing in, with this enrolment dialog for React with Base UI. It opens from the security page of Wardfield, a cloud code editor: the page, a list of rows with a switch each, is drawn under a soft scrim inside a hairline frame with a plus mark on each corner, and its header holds an outline Turn on two-step Button. The dialog is a stepper: a Steps header names Scan, Save codes and Verify. Step one shows a QR Code of a setup link beside the key to type if you can't scan, with a Copy Button. Step two lists eight backup codes with Copy all, a Download Trigger that saves them as a text file, and a required Checkbox that opens Next. Step three takes the six-digit code in an Input OTP. Next is the only Shine Button, and the last view is an Alert saying two-step sign-in is on in this demo, with the page's Two-step row turned On. For preview the dialog is drawn open in place when the page loads; the header's button opens the real Dialog. For security settings, accounts and any app with a second step.

FAQ

Why is the dialog already open, and what do I keep?

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. The Turn on two-step button 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 SetupFields component and share the block's state. 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 steps work?

The three views, and the closing note, share one grid cell with the unseen ones made invisible and inert, so the dialog keeps its height whichever you are on, and Back keeps what you typed or ticked. Next on the second step stays off until you tick that you saved the codes. The Steps header only shows where you are: it isn't a row of buttons, so the order can't be skipped.

Is the QR code real, and can I ship these codes?

The QR code is real and a phone's authenticator can scan it: it encodes an otpauth:// link with an account, an issuer and a key. But the key and the eight backup codes are typed in the file so the block is the same on every render, which means everyone sees the same ones. Never ship them: generate a key and codes for each account on your server and put them in place of SETUP_URI, SECRET and BACKUP_CODES.

What does the last step check?

Only that you typed 482913, the code in the hint, and it counts three tries: a wrong code says how many are left, and after the third the field locks until you go back or close the dialog. In your app verify the code on the server as a time-based one-time password (RFC 6238) against the key you stored for that person, allow a step of clock drift either way, switch two-step sign-in on only after a first code passes, and store the backup codes hashed.

What stays local?

Nothing is enrolled. Copy uses the real clipboard and Download saves a real text file of the typed codes, but a finished setup only turns the drawn Two-step row to On and says so in the polite status; the header's button becomes Start over, which turns it Off again to try another code.

Is it accessible from the keyboard?

Yes. The real dialog is named by its title and described by its sentence, focus starts on the first control of the first step and stays inside until it closes, and Escape, Cancel and the close button return focus to the header's button. Each step is announced by the polite status line, and the QR code carries a text alternative: the key beside it. The trigger is passed to DialogTrigger through render, and the dialog is controlled with open and onOpenChange. On a phone the copy shown on load docks to the foot of the page as a bottom sheet, the codes become one column and the body scrolls above the pinned actions; the real dialog keeps the Dialog's own phone layout.

  • Pro

    Deactivate 2FA Dialog

    Destructive confirms

    A security page with its turn-off confirmation shown open: an alert dialog that lists what goes with two-step verification, asks for the password and a ticked acknowledgement, and keeps its destructive action quiet until both are done.

  • Pro

    PIN Keypad Dialog

    Account and security

    A 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

    One-Time Code Input Form

    Numbers and pickers

    A six-slot confirmation code in two groups of three with its behaviours in view: paste fills all six and checks at once, a wrong code is marked, counted and cleared, and a resend button waits 30 seconds.

  • Pro

    An account-security prompt to turn on two-factor sign-in: three radio cards for the method, a QR code with a made-up key and a six-digit code field for the app, a masked number for text messages, and a Turn on button that accepts the printed demo code.

  • Pro

    Passkeys Account

    Sign-in methods

    An account's passkeys: a ruled list of what each one is, when it was created and last used, a name you edit in place, a guarded Remove, and a dialog from the header that names a new passkey.

  • Pro

    Session Timeout Dialog

    Status and errors

    A 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.