Skip to content

Migration

Version 8 keeps the original MuiOtpInput API and adds Base UI, shadcn, headless, and form entrypoints next to it.

From mui-one-time-password-input

This package continues viclafouch/mui-otp-input, published as mui-one-time-password-input. Swap the package and the import; your props keep working.

Terminal
pnpm remove mui-one-time-password-input
pnpm add @wh1teee/mui-otp-input
diff
- import { MuiOtpInput } from 'mui-one-time-password-input';
+ import { MuiOtpInput } from '@wh1teee/mui-otp-input';

Every prop of the original component — value, length, autoFocus, onChange, onComplete, onBlur, validateChar, and TextFieldsProps — keeps its name and meaning, and the root still accepts Material UI Box props. Material UI 7 and 9 and React 18 and 19 are supported.

What you gain

Optional props. Nothing changes until you use them.

PropWhat it does
validationTypeBuilt-in numeric or alphanumeric policy instead of a hand-written validateChar.
pastePreprocessCleans pasted and autofilled text before character rules run.
transformCharMaps characters before validation, for example to upper case.
onInvalidReports rejected typing and paste so you can explain why.
autoFocus={ms}Delays first-slot focus until a dialog or transition settles.
ariaLabel, slotAriaLabelNames the group and announces each slot position.
name, form, autoSubmitNative form submission and reset, opt-in submit on completion.
maskRenders slots as password inputs.
VerificationCode.tsx
// Before: digits were enforced by hand.
<MuiOtpInput
  length={6}
  value={code}
  onChange={setCode}
  validateChar={(character) => /^\d$/.test(character)}
/>

// After: the built-in policy, a cleaned paste, and a named group.
<MuiOtpInput
  ariaLabel="Verification code"
  length={6}
  value={code}
  onChange={setCode}
  validationType="numeric"
  pastePreprocess="digits-only"
/>

From @wh1teee/mui-otp-input 5.x

Root imports are unchanged. Version 8 rebased the fork onto upstream v7 and kept its behavior.

  • Delayed autoFocus, transformChar, pastePreprocess, and stable input refs behave as before.
  • /mui is an explicit alias of the root import, for codebases that want the renderer visible in every import.
  • The explicit form adapter path is /mui/react-hook-form; /react-hook-form still works.

Leaving Material UI

Move one screen at a time: both renderers share one behavior contract, so the value your form receives does not change.

diff
- import { MuiOtpInput } from '@wh1teee/mui-otp-input';
+ import { OtpInput } from '@wh1teee/mui-otp-input/shadcn';
+ import '@wh1teee/mui-otp-input/shadcn.css';

- <MuiOtpInput ariaLabel="Code" length={6} value={code} onChange={setCode} />
+ <OtpInput label="Code" length={6} value={code} onValueChange={setCode} />

The Base UI field renders its own label and description, defaults to numeric codes, and names change callbacks after Base UI (onValueChange, onValueComplete). Once no screen imports the root or /mui, remove @mui/material and Emotion — the Base UI entrypoints never load them.