Piplup
@piplup/rhf-adaptersmui-one-time-password-inputComponents

MuiOtpInputElement

A mui-one-time-password-input field connected to React Hook Form that stores the code as one string, with length checks and error display.

MuiOtpInputElement renders MuiOtpInput from mui-one-time-password-input and stores the code in React Hook Form as a single string, such as "482913". It accepts all MuiOtpInput props, including length, validateChar, onComplete and TextFieldsProps. To build your own code field, use useMuiOtpInputAdapter.

Import

import { MuiOtpInputElement } from "@piplup/rhf-adapters/mui-one-time-password-input";

Usage

"use client";import Stack from "@mui/material/Stack";import { MuiOtpInputElement } from "@piplup/rhf-adapters/mui-one-time-password-input";import { MuiButtonElement } from "@piplup/rhf-adapters/mui-material";import { useForm } from "react-hook-form";export default function Page() {  const { control, handleSubmit } = useForm({ defaultValues: { code: "" } });  return (    <form      onSubmit={handleSubmit((values) =>        alert(JSON.stringify(values, null, 2)),      )}    >      <Stack direction="column" spacing={2}>        <MuiOtpInputElement control={control} name="code" length={6} />        <div>          <MuiButtonElement control={control} type="submit" variant="contained">            Submit          </MuiButtonElement>        </div>      </Stack>    </form>  );}

Requiring a complete code

This demo accepts digits only, requires all six, and shows errors under the boxes. Submit with three digits filled in to see the length error.

We sent a 6-digit code to your email

"use client";import Stack from "@mui/material/Stack";import { MuiButtonElement, MuiFormHelperTextElement } from "@piplup/rhf-adapters/mui-material";import { MuiOtpInputElement } from "@piplup/rhf-adapters/mui-one-time-password-input";import { useForm } from "react-hook-form";export default function Page() {  const { control, handleSubmit } = useForm({ defaultValues: { code: "" } });  return (    <form      noValidate      onSubmit={handleSubmit((values) => alert(JSON.stringify(values, null, 2)))}    >      <Stack direction="column" spacing={1}>        <MuiOtpInputElement          control={control}          name="code"          length={6}          // Ignore anything that isn't a digit, including pasted text.          validateChar={(character) => /^\d$/.test(character)}          required          // A partly filled code is not empty, so `required` alone lets it through.          minLength={6}          messages={{            required: "Enter the code we sent you",            minLength: "Enter all 6 digits",          }}        />        {/* The OTP input has no helper text, so render the error message here. */}        <MuiFormHelperTextElement control={control} name="code">          We sent a 6-digit code to your email        </MuiFormHelperTextElement>        <div>          <MuiButtonElement control={control} variant="contained" type="submit">            Verify          </MuiButtonElement>        </div>      </Stack>    </form>  );}

Three things make that work:

  • minLength equal to length. The value is just the characters entered so far, so "482" is not empty and passes required. minLength={6} rejects it. The default message is "Please lengthen this text to 6 characters or more", so set messages.minLength.
  • validateChar. A MuiOtpInput prop that refuses characters as they are typed or pasted. It does not check values set through defaultValues or setValue; add pattern={/^\d+$/} if those can contain other characters.
  • A separate helper text. MuiOtpInput has no helper text, and the element ignores the helperText prop. Errors only turn the boxes red. To show the message, render MuiFormHelperTextElement with the same name. It shows its children normally and the error message while the field is invalid.

How it works

Value. The form value is passed to MuiOtpInput as value, and every change stores the full code string. MuiOtpInput only shows the first length characters, but a longer value set from code is stored as is. onComplete is passed through and fires when the last box is filled.

Errors. While the field is invalid, each box gets error. If you pass TextFieldsProps (an object or a function of the box index), its props are kept and error is added on top.

Blur. The field is marked as touched only when focus leaves the whole group, not when it moves from one box to the next.

Focus. React Hook Form's ref is attached to the first box, so setFocus("code") and focus-on-error move focus there. A ref on the element goes to the MuiOtpInput root Box.

Props

Prop

Type

On this page