Back to Heroui

InputOTP

apps/docs/content/docs/react/migration/(components)/input-otp.mdx

3.0.47.6 KB
Original Source
<Callout type="info"> Refer to the [v3 InputOTP documentation](/docs/react/components/input-otp) for complete API reference, styling guide, and advanced examples. This guide only focuses on migrating from HeroUI v2. </Callout>

Structure Changes

In v2, InputOtp automatically rendered segments based on the length prop:

tsx
import { InputOtp } from "@heroui/react";

export default function App() {
  return <InputOtp length={4} />;
}

In v3, InputOTP requires manual definition of slots using compound components:

tsx
import { InputOTP } from "@heroui/react";

export default function App() {
  return (
    <InputOTP maxLength={4}>
      <InputOTP.Group>
        <InputOTP.Slot index={0} />
        <InputOTP.Slot index={1} />
        <InputOTP.Slot index={2} />
        <InputOTP.Slot index={3} />
      </InputOTP.Group>
    </InputOTP>
  );
}

Key Changes

1. Component Structure

v2: Single component with automatic segment rendering
v3: Compound components: InputOTP.Group, InputOTP.Slot, InputOTP.Separator

2. Prop Changes

v2 Propv3 LocationNotes
lengthInputOTPRenamed to maxLength
allowedKeysInputOTPRenamed to pattern (regex)
onValueChangeInputOTPUse onChange
description, errorMessage-Handle with Description / FieldError
variantInputOTPSimplified to primary | secondary only
color, size, radius-Removed (use Tailwind CSS)
classNames-Use className on parts
-textAlignNew prop: text alignment within slots ('left' | 'center' | 'right')
-inputModeNew prop: virtual keyboard type on mobile (default 'numeric')
-placeholderNew prop: placeholder text for empty slots
-pasteTransformerNew prop: transform pasted text (e.g., remove hyphens)

Migration Examples

Controlled InputOTP

<Tabs items={["v2", "v3"]}> <Tab value="v2"> ```tsx import { useState } from "react";

const [value, setValue] = useState("");

<InputOtp length={4} value={value} onValueChange={setValue} />
```
</Tab> <Tab value="v3"> ```tsx import { useState } from "react";
const [value, setValue] = useState("");

<InputOTP maxLength={4} value={value} onChange={setValue}>
  <InputOTP.Group>
    <InputOTP.Slot index={0} />
    <InputOTP.Slot index={1} />
    <InputOTP.Slot index={2} />
    <InputOTP.Slot index={3} />
  </InputOTP.Group>
</InputOTP>
```
</Tab> </Tabs>

With Allowed Keys / Pattern

<Tabs items={["v2", "v3"]}> <Tab value="v2"> tsx <InputOtp allowedKeys="^[a-z]*$" length={4} /> </Tab> <Tab value="v3"> ```tsx import { REGEXP_ONLY_CHARS } from "@heroui/react";

<InputOTP maxLength={4} pattern={REGEXP_ONLY_CHARS}>
  <InputOTP.Group>
    <InputOTP.Slot index={0} />
    <InputOTP.Slot index={1} />
    <InputOTP.Slot index={2} />
    <InputOTP.Slot index={3} />
  </InputOTP.Group>
</InputOTP>
```
</Tab> </Tabs>

Form Validation

<Tabs items={["v2", "v3"]}> <Tab value="v2"> tsx <InputOtp description="Enter the code sent to your email" length={4} /> <InputOtp errorMessage="Invalid code" isInvalid length={4} /> </Tab> <Tab value="v3"> tsx import { Description, FieldError } from "@heroui/react"; <div className="flex flex-col gap-2"> <InputOTP maxLength={4}> <InputOTP.Group> <InputOTP.Slot index={0} /> <InputOTP.Slot index={1} /> <InputOTP.Slot index={2} /> <InputOTP.Slot index={3} /> </InputOTP.Group> </InputOTP> <Description>Enter the code sent to your email</Description> </div> <div className="flex flex-col gap-2"> <InputOTP isInvalid maxLength={4}> <InputOTP.Group> <InputOTP.Slot index={0} /> <InputOTP.Slot index={1} /> <InputOTP.Slot index={2} /> <InputOTP.Slot index={3} /> </InputOTP.Group> </InputOTP> <FieldError>Invalid code</FieldError> </div> </Tab> </Tabs>

With onComplete Callback

<Tabs items={["v2", "v3"]}> <Tab value="v2"> tsx <InputOtp length={6} onComplete={(value) => console.log("Complete:", value)} /> </Tab> <Tab value="v3"> tsx <InputOTP maxLength={6} onComplete={(value) => console.log("Complete:", value)} > <InputOTP.Group> <InputOTP.Slot index={0} /> <InputOTP.Slot index={1} /> <InputOTP.Slot index={2} /> </InputOTP.Group> <InputOTP.Separator /> <InputOTP.Group> <InputOTP.Slot index={3} /> <InputOTP.Slot index={4} /> <InputOTP.Slot index={5} /> </InputOTP.Group> </InputOTP> </Tab> </Tabs>

Component Anatomy

The v3 InputOTP follows this structure:

InputOTP (Root)
  ├── InputOTP.Group
  │   ├── InputOTP.Slot (index={0})
  │   ├── InputOTP.Slot (index={1})
  │   └── ...
  ├── InputOTP.Separator (optional)
  └── InputOTP.Group (optional, for grouping)
      └── InputOTP.Slot (index={...})

New Props in v3

v3 introduces several new props not available in v2:

  • textAlign: Controls text alignment within slots ('left' | 'center' | 'right', default 'left')
  • inputMode: Sets the virtual keyboard type on mobile devices ('numeric' | 'text' | 'decimal' | 'tel' | 'search' | 'email' | 'url', default 'numeric')
  • placeholder: Sets placeholder text for empty slots
  • pasteTransformer: A function (text: string) => string to transform pasted text (e.g., removing hyphens from a pasted code)
tsx
<InputOTP
  maxLength={6}
  inputMode="numeric"
  textAlign="center"
  placeholder="-"
  pasteTransformer={(text) => text.replace(/-/g, "")}
>
  <InputOTP.Group>
    <InputOTP.Slot index={0} />
    <InputOTP.Slot index={1} />
    <InputOTP.Slot index={2} />
  </InputOTP.Group>
  <InputOTP.Separator />
  <InputOTP.Group>
    <InputOTP.Slot index={3} />
    <InputOTP.Slot index={4} />
    <InputOTP.Slot index={5} />
  </InputOTP.Group>
</InputOTP>

Exported Regex Patterns

HeroUI re-exports common regex patterns from the input-otp library for convenience:

tsx
import { REGEXP_ONLY_DIGITS, REGEXP_ONLY_CHARS, REGEXP_ONLY_DIGITS_AND_CHARS } from "@heroui/react";

// Use with the pattern prop
<InputOTP pattern={REGEXP_ONLY_DIGITS} maxLength={6}>
</InputOTP>
  • REGEXP_ONLY_DIGITS - Only numeric characters (0-9)
  • REGEXP_ONLY_CHARS - Only alphabetic characters (a-z, A-Z)
  • REGEXP_ONLY_DIGITS_AND_CHARS - Alphanumeric characters (0-9, a-z, A-Z)

Summary

  1. Component Structure: Must manually define slots using InputOTP.Group and InputOTP.Slot
  2. length → maxLength: Prop renamed
  3. allowedKeys → pattern: Prop renamed, uses regex pattern
  4. onValueChange → onChange: Event handler renamed
  5. Description Removed: Handle separately with Description component
  6. Error Message Removed: Handle separately with error display
  7. Variant Simplified: v3 supports only variant="primary" and variant="secondary"
  8. Colors Removed: Use Tailwind CSS classes for styling
  9. Sizes Removed: Use Tailwind CSS classes for styling
  10. Radius Removed: Use Tailwind CSS classes for styling
  11. ClassNames Removed: Use className props on individual components
  12. New Props: v3 adds textAlign, inputMode, placeholder, and pasteTransformer props
  13. Exported Patterns: Use REGEXP_ONLY_DIGITS, REGEXP_ONLY_CHARS, and REGEXP_ONLY_DIGITS_AND_CHARS for the pattern prop