Pro--% off in--d : --h : --m : --s
HeroUI

NumberInput

Migration guide for NumberInput to NumberField from HeroUI v2 to v3

Refer to the v3 NumberField documentation for complete API reference, styling guide, and advanced examples. This guide only focuses on migrating from HeroUI v2.

Structure Changes

In v2, NumberInput was a single component with props:

import { NumberInput } from "@heroui/react";

export default function App() {
  return <NumberInput label="Amount" defaultValue={1024} />;
}

In v3, NumberField requires compound components:

import { NumberField, Label } from "@heroui/react";

export default function App() {
  return (
    <NumberField defaultValue={1024}>
      <Label>Amount</Label>
      <NumberField.Group>
        <NumberField.DecrementButton />
        <NumberField.Input />
        <NumberField.IncrementButton />
      </NumberField.Group>
    </NumberField>
  );
}

Key Changes

1. Component Naming

v2: NumberInput
v3: NumberField

2. Component Structure

v2: Single component with props
v3: Compound components: NumberField.Group, NumberField.Input, NumberField.IncrementButton, NumberField.DecrementButton

3. Prop Changes

v2 Propv3 LocationNotes
onValueChangeonChangeRenamed event handler
labelUse Label component
descriptionUse Description component
errorMessageUse FieldError component
variantvariant (on NumberField)Simplified to primary | secondary only
colorRemoved (use Tailwind CSS)
sizeRemoved (use Tailwind CSS)
radiusRemoved (use Tailwind CSS)
startContentPlace content manually in Group
endContentPlace content manually in Group
labelPlacementHandle with layout classes
hideStepperOmit NumberField.IncrementButton and NumberField.DecrementButton
isClearableHandle clear functionality manually
classNamesUse className props on individual components
isWheelDisabledRemoved

Migration Examples

Form Validation

{/* With description */}
<NumberInput
  description="Enter the amount"
  label="Amount"
/>

{/* With error message */}
<NumberInput
  errorMessage="Please enter a valid number"
  isInvalid
  label="Amount"
/>

{/* Required */}
<NumberInput isRequired label="Quantity" />
import { Description, FieldError, Label } from "@heroui/react";

{/* With description */}
<NumberField>
  <Label>Amount</Label>
  <NumberField.Group>
    <NumberField.DecrementButton />
    <NumberField.Input />
    <NumberField.IncrementButton />
  </NumberField.Group>
  <Description>Enter the amount</Description>
</NumberField>

{/* With error message */}
<NumberField isInvalid>
  <Label>Amount</Label>
  <NumberField.Group>
    <NumberField.DecrementButton />
    <NumberField.Input />
    <NumberField.IncrementButton />
  </NumberField.Group>
  <FieldError>Please enter a valid number</FieldError>
</NumberField>

{/* Required */}
<NumberField isRequired>
  <Label>Quantity</Label>
  <NumberField.Group>
    <NumberField.DecrementButton />
    <NumberField.Input />
    <NumberField.IncrementButton />
  </NumberField.Group>
</NumberField>

Controlled

import { useState } from "react";

const [value, setValue] = useState();

<NumberInput
  value={value}
  onValueChange={setValue}
/>
import { useState } from "react";

const [value, setValue] = useState<number | undefined>();

<NumberField value={value} onChange={setValue}>
  <Label>Amount</Label>
  <NumberField.Group>
    <NumberField.DecrementButton />
    <NumberField.Input />
    <NumberField.IncrementButton />
  </NumberField.Group>
</NumberField>

Without Stepper Buttons

<NumberInput hideStepper label="Amount" />
<NumberField>
  <Label>Amount</Label>
  <NumberField.Group>
    <NumberField.Input />
  </NumberField.Group>
</NumberField>

Number Constraints

{/* Min/Max */}
<NumberInput
  label="Quantity"
  maxValue={100}
  minValue={0}
/>

{/* Step */}
<NumberInput label="Percentage" step={0.1} />

{/* Format options */}
<NumberInput
  formatOptions={{style: "currency", currency: "USD"}}
  label="Price"
/>
{/* Min/Max */}
<NumberField maxValue={100} minValue={0}>
  <Label>Quantity</Label>
  <NumberField.Group>
    <NumberField.DecrementButton />
    <NumberField.Input />
    <NumberField.IncrementButton />
  </NumberField.Group>
</NumberField>

{/* Step */}
<NumberField step={0.1}>
  <Label>Percentage</Label>
  <NumberField.Group>
    <NumberField.DecrementButton />
    <NumberField.Input />
    <NumberField.IncrementButton />
  </NumberField.Group>
</NumberField>

{/* Format options */}
<NumberField formatOptions={{style: "currency", currency: "USD"}}>
  <Label>Price</Label>
  <NumberField.Group>
    <NumberField.DecrementButton />
    <NumberField.Input />
    <NumberField.IncrementButton />
  </NumberField.Group>
</NumberField>

Component Anatomy

The v3 NumberField follows this structure:

NumberField (Root)
  ├── Label (optional)
  ├── NumberField.Group
  │   ├── NumberField.DecrementButton
  │   ├── NumberField.Input
  │   └── NumberField.IncrementButton
  ├── Description (optional)
  └── FieldError (optional)

Summary

  1. Component Renamed: NumberInputNumberField
  2. Component Structure: Must use compound components (NumberField.Group, NumberField.Input, etc.)
  3. Label/Description/Error: Use separate components (Label, Description, FieldError)
  4. Stepper Buttons: Must explicitly include NumberField.IncrementButton and NumberField.DecrementButton
  5. Event Handler: onValueChangeonChange
  6. Variant Simplified: v3 supports only variant="primary" and variant="secondary"; color, size, radius removed — use Tailwind CSS
  7. Content Props Removed: startContent, endContent - place manually
  8. Clear Button Removed: isClearable removed - handle manually
  9. Stepper Control: hideStepper removed - omit buttons instead
  10. Label Placement Removed: labelPlacement removed - handle with layout

On this page