- Accordion
- Action Bar
- Action Toast
- Alert Banner
- Alert Dialog
- Alert
- Alphabet Index
- Amount Field
- Aspect Ratio
- Attachment
- Availability List
- Avatar
- Badge
- Bank Card
- Breadcrumb
- Bubble
- Button Group
- Button
- Calendar
- Camera Frame
- Card Design Picker
- Card Details
- Card Peek
- Card Stack
- Card
- Carousel
- Chart
- Chat Thread
- Checkbox
- Chip Group
- Choice Tile
- Code Block
- Code Dots
- Collapsible
- Combobox
- Command
- Context Menu
- Copy Field
- Countdown
- Country Select
- Data Table
- Date Field
- Date Picker
- Date Range Picker
- Dialog
- Direction
- Document Paper
- Drawer
- Dropdown Menu
- Empty
- Entity Avatar
- Field
- File Trigger
- File Upload Item
- Hover Card
- Input Group
- Input OTP
- Input
- Item
- Kbd
- Key Value
- Keypad
- Label
- Live Region
- Marker
- Message Scroller
- Message
- Money Amount
- Native Select
- Number Field
- Pagination
- Percent Split Editor
- Phone Field
- Popover
- Progress
- QR Code
- Questionnaire
- Radio Group
- Range Histogram
- Rating
- Resizable
- Scroll Area
- Search Field
- Select
- Separator
- Share Bar
- Sheet
- Sidebar
- Skeleton
- Slider
- Sonner
- Sortable List
- Spinner
- Status Timeline
- Step Header
- Steps List
- Swipe Row
- Switch
- Table
- Tabs
- Text Field
- Textarea
- Tier Ladder
- Toggle Group
- Toggle
- Token Search Field
- Tooltip
- Trace
- Transaction Row
- Usage Meter
Enter your passcode
Try 2580
"use client"
import * as React from "react"The CodeDots component shows how many digits of a PIN or passcode are entered, one dot per digit, without showing the digits. Use it on an unlock or confirm screen together with a Keypad. It is not an input: the caller keeps the code and passes the count. When people type a one-time code they can read, such as an SMS code, use InputOTP instead.
Installation#
pnpm dlx shadcn@latest add @dots/code-dots
Usage#
import { CodeDots } from "@/components/ui/code-dots"<CodeDots length={4} filled={code.length} />Features#
- One dot per digit, filled up to
filledand clamped tolength errorturns the dots destructive and shakes them once;successfills every dot- An
attemptcounter that replays the shake and the announcement on each wrong code - A polite live status that announces progress, errors and success; the dots themselves are hidden from assistive tech
- Two sizes and any code length
Progress#
Pass the number of digits entered as filled.
import { CodeDots } from "@/components/ui/code-dots"
export function CodeDotsProgress() {States#
Set state="error" after a wrong code and state="success" once it is accepted.
import { CodeDots } from "@/components/ui/code-dots"
export function CodeDotsStates() {Attempts#
Increase attempt on every rejected code. The shake replays and the error is announced again. Do not change the component's key for this: remounting the live region can swallow the announcement.
1 wrong attempt
"use client"
import * as React from "react"<CodeDots filled={4} state="error" attempt={attempt} />Sizes#
Use size="sm" for a code inside a sheet or dialog.
import { CodeDots } from "@/components/ui/code-dots"
export function CodeDotsSizes() {Length#
Set length for codes other than four digits, label to say what the code is, and errorMessage or successMessage to match it.
import { CodeDots } from "@/components/ui/code-dots"
export function CodeDotsLength() {Accessibility#
The dots are aria-hidden. A visually hidden role="status" region reads progress politely instead, for example "Passcode, 2 of 4 digits entered", then errorMessage or successMessage. It stays mounted so every change is announced.
Say what the code is#
label defaults to "Passcode". Change it for a card PIN or a verification code, and change errorMessage and successMessage to match.
<CodeDots length={6} filled={3} label="Verification code" errorMessage="Wrong code" />Pair it with labelled keys#
CodeDots takes no input. Pair it with a Keypad, whose keys each have their own accessible name, and show a visible heading such as "Enter your passcode". The demo at the top of this page does exactly that.
Meaning beyond color#
The error state uses a destructive color and a shake. Also show the reason in text, such as "Wrong passcode. Try again."
API Reference#
CodeDots#
| Prop | Type | Default | Description |
|---|---|---|---|
length | number | 4 | How many digits the code has. |
filled | number | 0 | How many digits are entered. Clamped to 0…length. |
state | "default" | "error" | "success" | "default" | "error" shakes the dots; "success" fills every dot. |
size | "default" | "sm" | "default" | The dot size. |
attempt | number | 0 | Wrong-attempt counter. Increase it to replay the error. |
label | string | "Passcode" | What the code is, for screen readers. |
errorMessage | string | "Wrong passcode" | Announced when state is "error". |
successMessage | string | "Passcode accepted" | Announced when state is "success". |
className | string | - | Additional classes to apply to the root element. |
...props | React.ComponentProps<"div"> | - | Props spread to the root div, except children. |