# OTP code input

> Six boxes for a verification code that jump forward as you type and back when you delete.

- Category: [Inputs](https://library.aimran.es/components/inputs)
- Built with: React + Motion
- Live editor: https://library.aimran.es/blocks/otp-six
- Library: AIMRAN Library (https://library.aimran.es), free, no sign-up, by Aimran Oukham

## Where to use it

One-time codes appear in sign-ups, two-factor login, bank transfers and delivery confirmations. Six separate boxes make the code easy to check at a glance, and the cursor moves on its own so people never have to tap each box. It only accepts digits, which is what most OTP input fields in React need.

Many people copy the code from an SMS or email, so consider adding paste support that fills all six boxes at once. Match the corner radius to your other inputs; round boxes look friendly, square ones feel more serious, which may suit a bank better.

## Code

### React (Motion)

```tsx
import { useRef, useState } from "react";

const LENGTH = 6;

interface OtpInputProps {
  radius?: number;
  value?: string;
  onChange?: (value: string) => void;
}

export default function OtpInput({ radius = 12, value, onChange }: OtpInputProps) {
  const [internal, setInternal] = useState<string[]>(Array(LENGTH).fill(""));
  const refs = useRef<Array<HTMLInputElement | null>>([]);
  const digits =
    value === undefined
      ? internal
      : Array.from({ length: LENGTH }, (_, i) => value[i] ?? "");

  const update = (i: number, x: string) => {
    const next = digits.map((d, j) => (j === i ? x : d));
    if (value === undefined) setInternal(next);
    onChange?.(next.join(""));
  };

  return (
    <div className="flex gap-2">
      {digits.map((d, i) => (
        <input
          key={i}
          ref={(el) => {
            refs.current[i] = el;
          }}
          aria-label={`Dígito ${i + 1}`}
          value={d}
          inputMode="numeric"
          maxLength={1}
          onChange={(e) => {
            const x = e.target.value.replace(/\D/g, "");
            update(i, x);
            if (x) refs.current[i + 1]?.focus();
          }}
          onKeyDown={(e) => {
            if (e.key === "Backspace" && !d) refs.current[i - 1]?.focus();
          }}
          className="h-12 w-10 border border-black/10 bg-white text-center text-lg text-[#1d1d1f] outline-none focus:ring-2 focus:ring-[#0071e3] dark:border-white/10 dark:bg-[#1c1c1e] dark:text-[#f5f5f7]"
          style={{ borderRadius: radius }}
        />
      ))}
    </div>
  );
}
```

## Prompt for AI assistants

Paste this into ChatGPT, Claude, Cursor, v0 or Lovable to adapt the block to your stack (Tailwind, Vue, Svelte…):

````text
I want to add the “OTP code input” block (Inputs) from AIMRAN Library to my project.

## What it is
Six boxes for a verification code that jump forward as you type and back when you delete.

## Chosen settings
- Radius: 12px (from 4 to 20px)

## What I need
1. Add it to my project with the same look and animation as the reference code: same sizes, colours, radii, shadows, timings and easing.
2. It is built with React + TypeScript, with framer-motion for the animation and lucide-react for the icons. If my project uses something else (Vue, Svelte, Astro, plain HTML…), translate it without losing any behaviour. If I use Tailwind, turn the styles into classes; if not, keep the CSS in its own file.
3. Expose the settings above as props (or CSS variables) with those values as defaults.
4. Make it work well on phones: nothing that depends only on hover, touch targets of at least 44 px and no overflow on 320 px screens.
5. Make it look right in light and dark mode.
6. Keep it accessible: semantic HTML, roles and aria-* where needed, visible focus and keyboard support.
7. Respect prefers-reduced-motion: with reduced motion, no big animations but the same end result.

## Reference code
```tsx
import { useRef, useState } from "react";

const LENGTH = 6;

interface OtpInputProps {
  radius?: number;
  value?: string;
  onChange?: (value: string) => void;
}

export default function OtpInput({ radius = 12, value, onChange }: OtpInputProps) {
  const [internal, setInternal] = useState<string[]>(Array(LENGTH).fill(""));
  const refs = useRef<Array<HTMLInputElement | null>>([]);
  const digits =
    value === undefined
      ? internal
      : Array.from({ length: LENGTH }, (_, i) => value[i] ?? "");

  const update = (i: number, x: string) => {
    const next = digits.map((d, j) => (j === i ? x : d));
    if (value === undefined) setInternal(next);
    onChange?.(next.join(""));
  };

  return (
    <div className="flex gap-2">
      {digits.map((d, i) => (
        <input
          key={i}
          ref={(el) => {
            refs.current[i] = el;
          }}
          aria-label={`Dígito ${i + 1}`}
          value={d}
          inputMode="numeric"
          maxLength={1}
          onChange={(e) => {
            const x = e.target.value.replace(/\D/g, "");
            update(i, x);
            if (x) refs.current[i + 1]?.focus();
          }}
          onKeyDown={(e) => {
            if (e.key === "Backspace" && !d) refs.current[i - 1]?.focus();
          }}
          className="h-12 w-10 border border-black/10 bg-white text-center text-lg text-[#1d1d1f] outline-none focus:ring-2 focus:ring-[#0071e3] dark:border-white/10 dark:bg-[#1c1c1e] dark:text-[#f5f5f7]"
          style={{ borderRadius: radius }}
        />
      ))}
    </div>
  );
}
```

Give me the complete final code, ready to paste, and tell me in one line where to put it in my project.
````
