Dashral Pro is on the wayGet 30% off at launch

Dashral

Input OTP

InputOTP with motion: each character pops into its box, the focus glides from box to box, an error shakes the row and a full code ripples.

$ pnpm dlx shadcn@latest add https://dashral.com/r/motion-input-otp.json

We sent a 6-digit code to +62 812 •••• 4821.

Installation

$ pnpm dlx shadcn@latest add https://dashral.com/r/motion-input-otp.json

Adds components/dashral/motion/input-otp.tsx, the Input OTP it builds on (in components/dashral/), motion and the motion files every motion component shares. With the registry in your components.json:

$ pnpm dlx shadcn@latest add @dashral/motion-input-otp

Usage

The same as Input OTP, imported from components/dashral/motion/: swap the import and it moves.

example.tsx
import { InputOTP } from "@/components/dashral/motion/input-otp"

<InputOTP label="Verification code" onComplete={(code) => verify(code)} />

Examples

Verify the code

A wrong code shakes the row and clears; the right one (123456) ripples through the boxes, then says so.

Try 123456.

A PIN

Masked dots pop in; a wrong PIN shakes; 2580 ripples and the lock pops open.

Try 2580.

Compared with Default

Default

Characters appear in their boxes; the focus ring jumps from box to box; an error turns the boxes red.

Motion

Each character pops in and shrinks out; the focus is one ring gliding from box to box; an error shakes the row; a full code ripples through the boxes.

How the two editions differ in general, and which to use where: Motion.

API reference

Every prop of Input OTP, and every attribute of the element it renders. The Motion edition adds no props of its own.

InputOTP

Also takes every prop of <input>(opens in a new tab).

PropTypeDefault
labelReactNode

Shown above, linked to the control.

—
descriptionReactNode

A hint under the control.

—
errorboolean | ReactNode

true shows the error style; a message also shows under the control.

—
labelActionReactNode

At the end of the label's row, e.g. a “Forgot password?” link or the current value.

—
withAsteriskboolean

Marks the label with an asterisk, e.g. alongside required.

false
lengthnumber

How many characters the code has.

6
value / defaultValuestring

Controlled or initial code.

—
onValueChange(value: string) => void

Called as it changes, with what's typed so far.

—
onComplete(value: string) => void

Called once every box is filled, e.g. to verify the code.

—
pattern"digits" | "alphanumeric"

What can be typed. Digits bring up the number keypad on phones; letters are uppercased.

"digits"
groupsnumber[]

Splits the boxes with a dash, e.g. [3, 3]. Ignored unless they add up to length.

—
maskboolean

Dots in place of the characters, for a PIN.

false
size"default" | "lg"

40px or 48px boxes.

"default"
classNames{ root, label, description, error, slots, slot }

Classes for each part.

—
classNamestring

Extra classes, merged with cn() so yours win.

—

Motion

  • Each character pops into its box from half its size with a small overshoot, and shrinks out when it's deleted.
  • The focus is one ring gliding from box to box as you type (motion's shared layout).
  • An error shakes the row a few pixels, again each time the message changes.
  • A full code ripples through the boxes: each lifts a little, 40ms after the one before.
  • With reduced motion characters only fade, and nothing shakes or ripples.