One-time code

Collects a one-time code in one cell per character. Supports auto-advance, backspace, and paste.

Code

Use it for

  • Numeric verification codes from SMS, mail, or an authenticator.
  • onComplete to submit as soon as the last cell fills.

Not for

  • Alphanumeric codes; the cells strip non-digits.
  • Passwords; use Input type="password".

Three ways in. They share one source, so the pixels match whichever you pick.

1. Import the package

npm install @noorddev/vlak-react

// once, next to your app's root
import "@noorddev/vlak-react/css";

import { InputOTP } from "@noorddev/vlak-react";

Precompiled React and one stylesheet. No compiler to configure. Per-component imports work too: @noorddev/vlak-react/components/input-otp.

2. Vendor the source

npx @noorddev/vlak-cli add input-otp

The StyleX leaf lands in components/vlak/, for your own compiler to own. See StyleX.

3. Through shadcn

npx shadcn add https://vlak.dev/r/input-otp.json

The same registry item, installed by shadcn's CLI.

CSS only

<link rel="stylesheet" href="node_modules/@noorddev/vlak/css/vlak.css" />

No React. Link vlak.css and use the markup and classes below.

import { InputOTP } from "@noorddev/vlak-react";

<InputOTP length={6} aria-label="One-time code" onComplete={(code) => verify(code)} />

InputOTP

One cell per character. Auto-advance, backspace, paste.

PropTypeDefaultDescription
lengthnumber6
onChange(code: string) => void
onComplete(code: string) => voidCalled once every cell is filled.
aria-labelstring"One-time code"Defines a string value that labels the current element.

Also accepts Omit<HTMLAttributes<HTMLDivElement>, "onChange">.

KeysDoes
Type a digitFills the cell and moves to the next
BackspaceClears the cell; on an empty cell moves back and clears the previous one
Arrow left, Arrow rightMoves between cells
PasteFills from the current cell onward
<div class="rs-otp" role="group" aria-label="One-time code"><input class="rs-otp-cell" maxlength="1" value="8" aria-label="Digit 1" /><input class="rs-otp-cell" maxlength="1" value="2" aria-label="Digit 2" /><input class="rs-otp-cell" maxlength="1" aria-label="Digit 3" /><input class="rs-otp-cell" maxlength="1" aria-label="Digit 4" /></div>
.rs-otp.rs-otp-cell.rs-otp-cell-invalid