Klarhimmel UI

Input OTP

Stable

A code split into boxes that behave as one field: paste fills them all, Backspace goes back, autofill works.

npx shadcn add @klarhimmel/input-otp

When to use

  • Use for

    A short code that was sent to the person: six digits by email or text, a booking reference.

  • Not for

    A long secret or a password. Use Input.

  • Pairs with

    Button, a resend link, a status line.

Preview

Enter the six-digit code we emailed you

0 of 6 digits.

input-otp.tsx
import { Theme } from '@/components/klarhimmel/theme/style';
import { InputOtp } from '@/components/klarhimmel/ui/input-otp';

export default function Example() {
  return (
    <Theme theme="brand">
      <InputOtp aria-label="Verification code" onComplete={(code) => console.log(code)} />
    </Theme>
  );
}

Install

Import

import { InputOtp } from '@/components/klarhimmel/ui/input-otp';
Lands in
components/klarhimmel/ui/input-otp.tsx, plus cn() and the theme item if they are missing. Import theme.css after Tailwind.
Render in
Render it inside <Theme>, once near the root of your app. Tokens and fonts are scoped to it, so outside it the component is unstyled.

Examples

  • States

    Wrong code

    That code is not right or has expired. Ask for a new one.

    Verifying

    import { InputOtp } from '@/components/klarhimmel/ui/input-otp';
    
    export default function InputOtpStates() {
      return (
        <div className="flex w-[min(24rem,calc(100cqw-3rem))] flex-col gap-6">
          <div className="flex flex-col gap-2">
            <p id="otp-err" className="text-kh-small font-medium">Wrong code</p>
            <InputOtp aria-labelledby="otp-err" aria-describedby="otp-err-msg" defaultValue="123456" invalid />
            <p id="otp-err-msg" className="text-kh-small text-kh-ink">That code is not right or has expired. Ask for a new one.</p>
          </div>
          <div className="flex flex-col gap-2">
            <p id="otp-off" className="text-kh-small font-medium">Verifying</p>
            <InputOtp aria-labelledby="otp-off" defaultValue="482913" disabled />
          </div>
        </div>
      );
    }
  • Form

    Booking reference

    Letters and digits. Paste works too.

    import { useState } from 'react';
    import { Button } from '@/components/klarhimmel/ui/button';
    import { InputOtp } from '@/components/klarhimmel/ui/input-otp';
    
    export default function InputOtpForm() {
      const [code, setCode] = useState('');
      const [sent, setSent] = useState('');
      return (
        <form
          onSubmit={(e) => {
            e.preventDefault();
            setSent(code);
          }}
          className="flex w-[min(24rem,calc(100cqw-3rem))] flex-col gap-3"
        >
          <p id="otp-form" className="text-kh-small font-medium">Booking reference</p>
          <InputOtp aria-labelledby="otp-form" type="alphanumeric" length={5} name="reference" value={code} onValueChange={(v) => setCode(v.toUpperCase())} />
          <Button type="submit" disabled={code.length < 5}>Find booking</Button>
          <p role="status" className="text-kh-small text-kh-muted">{sent ? `Looking for ${sent}.` : 'Letters and digits. Paste works too.'}</p>
        </form>
      );
    }

API reference

PropTypeDefaultDescription
lengthnumber6How many boxes.
value / defaultValue / onValueChangestring / (value: string) => void-Controlled or uncontrolled. The value is one string without gaps.
onComplete(value: string) => void-Called once when every box is filled.
type'numeric' | 'alphanumeric''numeric'Digits only (with the numeric keypad), or letters and digits.
namestring-Submits the whole code with a form (a hidden input).
invalid / disabled / readOnlybooleanfalseError styling on every box; switches them off.
aria-label / aria-labelledbystring-Required: names the group.
boxLabel(n: number, of: number) => string'Digit n of N'The name of each box. Pass it translated.
classNamestring-Extra classes, merged after the component's own.

Accessibility

KeyDoes
TypeFills the box and moves to the next; letters are ignored in numeric mode
PasteFills from the box you pasted into, ignoring spaces
BackspaceClears the box; in an empty box removes the one before and moves back
DeleteRemoves the character in the box
Left / Right / Home / EndMove between boxes

The first box carries autocomplete one-time-code, so phones offer the code from a message. Each box is named "Digit n of N".

Accessibility