---
title: NumberInput
description: Migration guide for NumberInput to NumberField from HeroUI v2 to v3
---

<Callout type="info">
  Refer to the [v3 NumberField documentation](/docs/react/components/numberfield) for complete API reference, styling guide, and advanced examples. This guide only focuses on migrating from HeroUI v2.
</Callout>

## Structure Changes

In v2, NumberInput was a single component with props:

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

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

In v3, NumberField requires compound components:

```tsx
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 Prop | v3 Location | Notes |
|---------|-------------|-------|
| `onValueChange` | `onChange` | Renamed event handler |
| `label` | — | Use `Label` component |
| `description` | — | Use `Description` component |
| `errorMessage` | — | Use `FieldError` component |
| `variant` | `variant` (on NumberField) | Simplified to `primary` \| `secondary` only |
| `color` | — | Removed (use Tailwind CSS) |
| `size` | — | Removed (use Tailwind CSS) |
| `radius` | — | Removed (use Tailwind CSS) |
| `startContent` | — | Place content manually in Group |
| `endContent` | — | Place content manually in Group |
| `labelPlacement` | — | Handle with layout classes |
| `hideStepper` | — | Omit `NumberField.IncrementButton` and `NumberField.DecrementButton` |
| `isClearable` | — | Handle clear functionality manually |
| `classNames` | — | Use `className` props on individual components |
| `isWheelDisabled` | — | Removed |

## Migration Examples

### Form Validation

<Tabs items={["v2", "v3"]}>
  <Tab value="v2">
    ```tsx
    {/* 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" />
    ```
  </Tab>
  <Tab value="v3">
    ```tsx
    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>
    ```
  </Tab>
</Tabs>

### Controlled

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

    const [value, setValue] = useState();

    <NumberInput
      value={value}
      onValueChange={setValue}
    />
    ```
  </Tab>
  <Tab value="v3">
    ```tsx
    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>
    ```
  </Tab>
</Tabs>

### Without Stepper Buttons

<Tabs items={["v2", "v3"]}>
  <Tab value="v2">
    ```tsx
    <NumberInput hideStepper label="Amount" />
    ```
  </Tab>
  <Tab value="v3">
    ```tsx
    <NumberField>
      <Label>Amount</Label>
      <NumberField.Group>
        <NumberField.Input />
      </NumberField.Group>
    </NumberField>
    ```
  </Tab>
</Tabs>

### Number Constraints

<Tabs items={["v2", "v3"]}>
  <Tab value="v2">
    ```tsx
    {/* 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"
    />
    ```
  </Tab>
  <Tab value="v3">
    ```tsx
    {/* 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>
    ```
  </Tab>
</Tabs>

## 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**: `NumberInput` → `NumberField`
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**: `onValueChange` → `onChange`
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
