Skip to content
shadcn.io

shadcn.io is not affiliated with official shadcn/ui

PIN Keypad Dialog Block for React

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.

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

Approve a payment with a PIN the way a phone does, with this keypad dialog for React with Base UI. It opens from the transfer review of Purseline, a mobile wallet: the page's header holds an outline Approve Button and the review itself, a phone with an amount, a recipient and the fees, is drawn under a soft scrim inside a hairline frame with a plus mark on each corner. The dialog centres a lock, a title and four masked slots from Input OTP above a keypad of twelve Buttons: 1 to 9, Clear, 0 and Backspace, each 48 px. A digit typed on the keyboard fills the same slots, so the keypad and the keyboard are one value, and the fourth digit approves by itself, so there is no filled button to press. A wrong PIN marks the slots, nudges them once and says how many tries are left; after the third the keys lock. For preview the dialog is drawn open in place when the page loads; the Approve button opens the real Dialog. For wallets, banking, payments and any step that a short code unlocks.

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 Approve 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 PinFields component and share the block's state, so digits keyed 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 do the keypad and the keyboard share one value?

There is one string of up to four digits in the block's state. A key press appends to it, Backspace removes the last digit, Clear empties it, and typing or pasting into the slots replaces it, with anything that isn't a digit dropped. When the fourth digit lands the PIN is checked at once. There is no submit button on purpose: a keypad PIN is finished by its last digit, and Cancel stays for the way out.

Are the digits really hidden?

They are masked on every target and never read out: the line under the slots, which a screen reader announces when it appears, speaks counts only ("Wrong PIN. 2 tries left.") and never a digit. The slots are drawn by the input library, so the block draws a dot for each digit itself and hides the characters; the field behind them is a password input. The masking is a courtesy on a screen anyone might look at, and the demo PIN is printed in the hint on purpose.

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

Nothing is sent or checked anywhere. The right PIN, 2468, only turns the page's transfer row into "Sent to Tove Haldane: approved with PIN (demo)", and the header's button becomes Review again. In your app send the PIN to your server in settle, let the server count the tries and lock the account (the three here are only a demo), and move money only on its answer.

Is it accessible from the keyboard?

Yes. The real dialog is named by its title and described by its sentence, focus starts in the first slot so digits can be typed at once, and Escape, Cancel and the close button return focus to the Approve button. The keypad is a group named Keypad of real buttons, each at least 48 px, and Backspace has a name for a screen reader. The trigger is passed to DialogTrigger through render, and the dialog is controlled with open and onOpenChange. When the keys lock, focus moves to Cancel.

How does it look on a phone?

The copy shown on load docks to the foot of the page as a bottom sheet, with Cancel pinned under a body that scrolls. The real dialog keeps the Dialog's own phone layout, a sheet stuck to the bottom of the screen, and the keypad fills its width. The nudge is dropped under reduced motion, and the line says the same thing.

  • Pro

    Email Verification Dialog

    Account and security

    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.

  • 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

    Quick Transfer Dialog

    Payments and plans

    A payments app's account home with its send-money dialog shown open: five recent people in a scrolling row, an amount with quick chips, a note, the fee and balance after, and a review step before Send.

  • Pro

    A hosting dashboard with an alert dialog shown open that asks for the password again before a project is deleted: a lock tile, one field, a Caps Lock note, a count of tries and a destructive confirm.

  • Pro

    Two-Step Setup Dialog

    Account and security

    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.

  • Pro

    Add Profile Dialog

    Account and security

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