# Number Input

URL: https://ark-ui.com/docs/components/number-input
LLM: https://ark-ui.com/llms.txt/components/number-input

A field that allows user input of numeric values.

---



## Anatomy



```tsx
<NumberInput.Root>
  <NumberInput.Label />
  <NumberInput.Scrubber />
  <NumberInput.Control>
    <NumberInput.Input />
    <NumberInput.IncrementTrigger />
    <NumberInput.DecrementTrigger />
  </NumberInput.Control>
</NumberInput.Root>
```

## Examples

```tsx
import { NumberInput } from '@ark-ui/react/number-input'
import { ChevronDownIcon, ChevronUpIcon } from 'lucide-react'
import styles from 'styles/number-input.module.css'

export const Basic = () => (
  <NumberInput.Root className={styles.Root}>
    <NumberInput.Label className={styles.Label}>Label</NumberInput.Label>
    <NumberInput.Control className={styles.Control}>
      <NumberInput.Input className={styles.Input} />
      <div className={styles.TriggerGroup}>
        <NumberInput.IncrementTrigger className={styles.IncrementTrigger}>
          <ChevronUpIcon />
        </NumberInput.IncrementTrigger>
        <NumberInput.DecrementTrigger className={styles.DecrementTrigger}>
          <ChevronDownIcon />
        </NumberInput.DecrementTrigger>
      </div>
    </NumberInput.Control>
  </NumberInput.Root>
)
```

### Min and Max

Pass the `min` prop or `max` prop to set an upper and lower limit for the input. By default, the input will restrict the
value to stay within the specified range.

```tsx
import { NumberInput } from '@ark-ui/react/number-input'
import { ChevronDownIcon, ChevronUpIcon } from 'lucide-react'
import styles from 'styles/number-input.module.css'

export const MinMax = () => (
  <NumberInput.Root className={styles.Root} min={0} max={10}>
    <NumberInput.Label className={styles.Label}>Label</NumberInput.Label>
    <NumberInput.Control className={styles.Control}>
      <NumberInput.Input className={styles.Input} />
      <div className={styles.TriggerGroup}>
        <NumberInput.IncrementTrigger className={styles.IncrementTrigger}>
          <ChevronUpIcon />
        </NumberInput.IncrementTrigger>
        <NumberInput.DecrementTrigger className={styles.DecrementTrigger}>
          <ChevronDownIcon />
        </NumberInput.DecrementTrigger>
      </div>
    </NumberInput.Control>
  </NumberInput.Root>
)
```

> To allow values outside the min/max range, set `clampValueOnBlur` to `false`.

### Precision

In some cases, you might need the value to be rounded to specific decimal points. Set the `formatOptions` and provide
`Intl.NumberFormatOptions` such as `maximumFractionDigits` or `minimumFractionDigits`.

```tsx
import { NumberInput } from '@ark-ui/react/number-input'
import { ChevronDownIcon, ChevronUpIcon } from 'lucide-react'
import styles from 'styles/number-input.module.css'

export const FractionDigits = () => (
  <NumberInput.Root
    className={styles.Root}
    formatOptions={{ minimumFractionDigits: 2, maximumFractionDigits: 3 }}
    defaultValue="1.00"
  >
    <NumberInput.Label className={styles.Label}>Label</NumberInput.Label>
    <NumberInput.Control className={styles.Control}>
      <NumberInput.Input className={styles.Input} />
      <div className={styles.TriggerGroup}>
        <NumberInput.IncrementTrigger className={styles.IncrementTrigger}>
          <ChevronUpIcon />
        </NumberInput.IncrementTrigger>
        <NumberInput.DecrementTrigger className={styles.DecrementTrigger}>
          <ChevronDownIcon />
        </NumberInput.DecrementTrigger>
      </div>
    </NumberInput.Control>
  </NumberInput.Root>
)
```

### Scrubbing

The NumberInput supports the scrubber interaction pattern. To use this pattern, render the `NumberInput.Scrubber`
component. It uses the Pointer lock API and tracks the pointer movement. It also renders a virtual cursor which mimics
the real cursor's pointer.

```tsx
import { NumberInput } from '@ark-ui/react/number-input'
import { ChevronDownIcon, ChevronUpIcon, ArrowLeftRightIcon } from 'lucide-react'
import styles from 'styles/number-input.module.css'

export const Scrubber = () => (
  <NumberInput.Root className={styles.Root} defaultValue="32">
    <NumberInput.Label className={styles.Label}>Label</NumberInput.Label>
    <NumberInput.Control className={styles.Control}>
      <NumberInput.Scrubber className={styles.Scrubber}>
        <ArrowLeftRightIcon />
      </NumberInput.Scrubber>
      <NumberInput.Input className={styles.Input} data-has-scrubber />
      <div className={styles.TriggerGroup}>
        <NumberInput.IncrementTrigger className={styles.IncrementTrigger}>
          <ChevronUpIcon />
        </NumberInput.IncrementTrigger>
        <NumberInput.DecrementTrigger className={styles.DecrementTrigger}>
          <ChevronDownIcon />
        </NumberInput.DecrementTrigger>
      </div>
    </NumberInput.Control>
  </NumberInput.Root>
)
```

### Mouse Wheel

The NumberInput exposes a way to increment/decrement the value using the mouse wheel event. To activate this, set the
`allowMouseWheel` prop to `true`.

```tsx
import { NumberInput } from '@ark-ui/react/number-input'
import { ChevronDownIcon, ChevronUpIcon } from 'lucide-react'
import styles from 'styles/number-input.module.css'

export const MouseWheel = () => (
  <NumberInput.Root className={styles.Root} allowMouseWheel>
    <NumberInput.Label className={styles.Label}>Label</NumberInput.Label>
    <NumberInput.Control className={styles.Control}>
      <NumberInput.Input className={styles.Input} />
      <div className={styles.TriggerGroup}>
        <NumberInput.IncrementTrigger className={styles.IncrementTrigger}>
          <ChevronUpIcon />
        </NumberInput.IncrementTrigger>
        <NumberInput.DecrementTrigger className={styles.DecrementTrigger}>
          <ChevronDownIcon />
        </NumberInput.DecrementTrigger>
      </div>
    </NumberInput.Control>
  </NumberInput.Root>
)
```

### Formatting

To apply custom formatting to the input's value, set the `formatOptions` and provide `Intl.NumberFormatOptions` such as
`style` and `currency`.

```tsx
import { NumberInput } from '@ark-ui/react/number-input'
import { ChevronDownIcon, ChevronUpIcon } from 'lucide-react'
import styles from 'styles/number-input.module.css'

export const Formatting = () => (
  <NumberInput.Root
    className={styles.Root}
    formatOptions={{
      style: 'currency',
      currency: 'USD',
    }}
  >
    <NumberInput.Label className={styles.Label}>Label</NumberInput.Label>
    <NumberInput.Control className={styles.Control}>
      <NumberInput.Input className={styles.Input} />
      <div className={styles.TriggerGroup}>
        <NumberInput.IncrementTrigger className={styles.IncrementTrigger}>
          <ChevronUpIcon />
        </NumberInput.IncrementTrigger>
        <NumberInput.DecrementTrigger className={styles.DecrementTrigger}>
          <ChevronDownIcon />
        </NumberInput.DecrementTrigger>
      </div>
    </NumberInput.Control>
  </NumberInput.Root>
)
```

### Field

The `Field` component helps manage form-related state and accessibility attributes of a number input. It includes
handling ARIA labels, helper text, and error text to ensure proper accessibility.

```tsx
import { Field } from '@ark-ui/react/field'
import { NumberInput } from '@ark-ui/react/number-input'
import { ChevronDownIcon, ChevronUpIcon } from 'lucide-react'
import field from 'styles/field.module.css'
import styles from 'styles/number-input.module.css'

export const WithField = () => (
  <Field.Root className={field.Root}>
    <NumberInput.Root className={styles.Root}>
      <NumberInput.Label className={styles.Label}>Label</NumberInput.Label>
      <NumberInput.Control className={styles.Control}>
        <NumberInput.Input className={styles.Input} />
        <div className={styles.TriggerGroup}>
          <NumberInput.IncrementTrigger className={styles.IncrementTrigger}>
            <ChevronUpIcon />
          </NumberInput.IncrementTrigger>
          <NumberInput.DecrementTrigger className={styles.DecrementTrigger}>
            <ChevronDownIcon />
          </NumberInput.DecrementTrigger>
        </div>
      </NumberInput.Control>
    </NumberInput.Root>
    <Field.HelperText className={field.HelperText}>Additional Info</Field.HelperText>
    <Field.ErrorText className={field.ErrorText}>Error Info</Field.ErrorText>
  </Field.Root>
)
```

### Root Provider

An alternative way to control the number input is to use the `RootProvider` component and the `useNumberInput` hook.
This way you can access the state and methods from outside the component.

```tsx
import { NumberInput, useNumberInput } from '@ark-ui/react/number-input'
import { ChevronDownIcon, ChevronUpIcon } from 'lucide-react'
import styles from 'styles/number-input.module.css'

export const RootProvider = () => {
  const numberInput = useNumberInput()
  return (
    <div className="stack">
      <output>valueAsNumber: {numberInput.valueAsNumber}</output>
      <NumberInput.RootProvider className={styles.Root} value={numberInput}>
        <NumberInput.Label className={styles.Label}>Label</NumberInput.Label>
        <NumberInput.Control className={styles.Control}>
          <NumberInput.Input className={styles.Input} />
          <div className={styles.TriggerGroup}>
            <NumberInput.IncrementTrigger className={styles.IncrementTrigger}>
              <ChevronUpIcon />
            </NumberInput.IncrementTrigger>
            <NumberInput.DecrementTrigger className={styles.DecrementTrigger}>
              <ChevronDownIcon />
            </NumberInput.DecrementTrigger>
          </div>
        </NumberInput.Control>
      </NumberInput.RootProvider>
    </div>
  )
}
```

## Guides

### Scrubber

The `NumberInput.Scrubber` component provides an interactive scrub area that allows users to drag to change the input
value. It renders as a `<div>` element and displays a custom cursor element during scrubbing interactions.

This component utilizes the [Pointer Lock API](https://developer.mozilla.org/en-US/docs/Web/API/Pointer_Lock_API) for
smooth dragging interactions.

> **Note:** Browsers may show a notification when the Pointer Lock API is activated. The scrubber is automatically
> disabled in Safari to prevent layout shifts.

### Controlled

When controlling the NumberInput component, it's recommended to use string values instead of converting to numbers. This
is especially important when using `formatOptions` for currency or locale-specific formatting.

```tsx
const [value, setValue] = useState('0')

<NumberInput.Root value={value} onValueChange={(details) => setValue(details.value)}>
  {/* ... */}
</NumberInput.Root>
```

Converting values to numbers can cause issues with locale-specific formatting, particularly for currencies that use
different decimal and thousands separators (e.g., `1.523,30` vs `1,523.30`). By keeping values as strings, you preserve
the correct formatting and avoid parsing issues.

If you need to submit a numeric value in your form, use a hidden input that reads `valueAsNumber` from
`NumberInput.Context`:

```tsx
<NumberInput.Root value={value} onValueChange={(details) => setValue(details.value)}>
  <NumberInput.Input />
  <NumberInput.Context>
    {(context) => <input type="hidden" name="amount" value={context.valueAsNumber} />}
  </NumberInput.Context>
</NumberInput.Root>
```

## API Reference

### Props

### Root

#### Props

**`allowMouseWheel`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Whether to allow mouse wheel to change the value

**`allowOverflow`**
Type: `boolean`
Required: false
Default Value: `true`
Description: Whether to allow the value overflow the min/max range

**`asChild`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Use the provided child element as the default rendered element, combining their props and behavior.

**`clampValueOnBlur`**
Type: `boolean`
Required: false
Default Value: `true`
Description: Whether to clamp the value when the input loses focus (blur)

**`defaultValue`**
Type: `string`
Required: false
Default Value: `undefined`
Description: The initial value of the input when rendered.
Use when you don't need to control the value of the input.

**`disabled`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Whether the number input is disabled.

**`focusInputOnChange`**
Type: `boolean`
Required: false
Default Value: `true`
Description: Whether to focus input when the value changes

**`form`**
Type: `string`
Required: false
Default Value: `undefined`
Description: The associate form of the input element.

**`formatOptions`**
Type: `NumberFormatOptions`
Required: false
Default Value: `undefined`
Description: The options to pass to the `Intl.NumberFormat` constructor

**`id`**
Type: `string`
Required: false
Default Value: `undefined`
Description: The unique identifier of the machine.

**`ids`**
Type: `Partial<{
  root: string
  label: string
  input: string
  incrementTrigger: string
  decrementTrigger: string
  scrubber: string
}>`
Required: false
Default Value: `undefined`
Description: The ids of the elements in the number input. Useful for composition.

**`inputMode`**
Type: `InputMode`
Required: false
Default Value: `"decimal"`
Description: Hints at the type of data that might be entered by the user. It also determines
the type of keyboard shown to the user on mobile devices

**`invalid`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Whether the number input value is invalid.

**`largeStep`**
Type: `number`
Required: false
Default Value: `10 * step`
Description: The amount to increment or decrement the value by when the `Shift` key is held.

**`locale`**
Type: `string`
Required: false
Default Value: `"en-US"`
Description: The current locale. Based on the BCP 47 definition.

**`max`**
Type: `number`
Required: false
Default Value: `Number.MAX_SAFE_INTEGER`
Description: The maximum value of the number input

**`min`**
Type: `number`
Required: false
Default Value: `Number.MIN_SAFE_INTEGER`
Description: The minimum value of the number input

**`name`**
Type: `string`
Required: false
Default Value: `undefined`
Description: The name attribute of the number input. Useful for form submission.

**`onFocusChange`**
Type: `(details: FocusChangeDetails) => void`
Required: false
Default Value: `undefined`
Description: Function invoked when the number input is focused

**`onValueChange`**
Type: `(details: ValueChangeDetails) => void`
Required: false
Default Value: `undefined`
Description: Function invoked when the value changes

**`onValueCommit`**
Type: `(details: ValueChangeDetails) => void`
Required: false
Default Value: `undefined`
Description: Function invoked when the value is committed (when the input is blurred or the Enter key is pressed)

**`onValueInvalid`**
Type: `(details: ValueInvalidDetails) => void`
Required: false
Default Value: `undefined`
Description: Function invoked when the value overflows or underflows the min/max range

**`pattern`**
Type: `string`
Required: false
Default Value: `"-?[0-9]*(.[0-9]+)?"`
Description: The pattern used to check the <input> element's value against

**`readOnly`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Whether the number input is readonly

**`required`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Whether the number input is required

**`smallStep`**
Type: `number`
Required: false
Default Value: `step / 10`
Description: The amount to increment or decrement the value by when the `Alt` key is held.

**`spinOnPress`**
Type: `boolean`
Required: false
Default Value: `true`
Description: Whether to spin the value when the increment/decrement button is pressed

**`step`**
Type: `number`
Required: false
Default Value: `1`
Description: The amount to increment or decrement the value by

**`translations`**
Type: `IntlTranslations`
Required: false
Default Value: `undefined`
Description: Specifies the localized strings that identifies the accessibility elements and their states

**`value`**
Type: `string`
Required: false
Default Value: `undefined`
Description: The controlled value of the input

#### Data Attributes

**`data-scope`**: number-input
**`data-part`**: root
**`data-disabled`**: Present when disabled
**`data-focus`**: Present when focused
**`data-invalid`**: Present when invalid
**`data-scrubbing`**: 

### Control

#### Props

**`asChild`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Use the provided child element as the default rendered element, combining their props and behavior.

#### Data Attributes

**`data-scope`**: number-input
**`data-part`**: control
**`data-focus`**: Present when focused
**`data-disabled`**: Present when disabled
**`data-invalid`**: Present when invalid
**`data-scrubbing`**: 

### DecrementTrigger

#### Props

**`asChild`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Use the provided child element as the default rendered element, combining their props and behavior.

#### Data Attributes

**`data-scope`**: number-input
**`data-part`**: decrement-trigger
**`data-disabled`**: Present when disabled
**`data-scrubbing`**: 

### IncrementTrigger

#### Props

**`asChild`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Use the provided child element as the default rendered element, combining their props and behavior.

#### Data Attributes

**`data-scope`**: number-input
**`data-part`**: increment-trigger
**`data-disabled`**: Present when disabled
**`data-scrubbing`**: 

### Input

#### Props

**`asChild`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Use the provided child element as the default rendered element, combining their props and behavior.

#### Data Attributes

**`data-scope`**: number-input
**`data-part`**: input
**`data-invalid`**: Present when invalid
**`data-disabled`**: Present when disabled
**`data-scrubbing`**: 

### Label

#### Props

**`asChild`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Use the provided child element as the default rendered element, combining their props and behavior.

#### Data Attributes

**`data-scope`**: number-input
**`data-part`**: label
**`data-disabled`**: Present when disabled
**`data-focus`**: Present when focused
**`data-invalid`**: Present when invalid
**`data-required`**: Present when required
**`data-scrubbing`**: 

### RootProvider

#### Props

**`value`**
Type: `UseNumberInputReturn`
Required: true
Default Value: `undefined`
Description: undefined

**`asChild`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Use the provided child element as the default rendered element, combining their props and behavior.

### Scrubber

#### Props

**`asChild`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Use the provided child element as the default rendered element, combining their props and behavior.

#### Data Attributes

**`data-scope`**: number-input
**`data-part`**: scrubber
**`data-disabled`**: Present when disabled
**`data-scrubbing`**: 

### ValueText

#### Props

**`asChild`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Use the provided child element as the default rendered element, combining their props and behavior.

#### Data Attributes

**`data-scope`**: number-input
**`data-part`**: value-text
**`data-disabled`**: Present when disabled
**`data-invalid`**: Present when invalid
**`data-focus`**: Present when focused
**`data-scrubbing`**: 

### Context

**API:**

| Property | Type | Description |
|----------|------|-------------|
| `focused` | `boolean` | Whether the input is focused. |
| `invalid` | `boolean` | Whether the input is invalid. |
| `empty` | `boolean` | Whether the input value is empty. |
| `value` | `string` | The formatted value of the input. |
| `valueAsNumber` | `number` | The value of the input as a number. |
| `setValue` | `(value: number) => void` | Function to set the value of the input. |
| `clearValue` | `VoidFunction` | Function to clear the value of the input. |
| `increment` | `VoidFunction` | Function to increment the value of the input by the step. |
| `decrement` | `VoidFunction` | Function to decrement the value of the input by the step. |
| `setToMax` | `VoidFunction` | Function to set the value of the input to the max. |
| `setToMin` | `VoidFunction` | Function to set the value of the input to the min. |
| `focus` | `VoidFunction` | Function to focus the input. |


## Accessibility

Complies with the [Spinbutton WAI-ARIA design pattern](https://www.w3.org/WAI/ARIA/apg/patterns/spinbutton/).

### Keyboard Support

**`ArrowUp`**
Description: Increments the value of the number input by a predefined step.

**`ArrowDown`**
Description: Decrements the value of the number input by a predefined step.

**`Shift + ArrowUp`**
Description: Increments the value of the number input by the `largeStep` amount.

**`Shift + ArrowDown`**
Description: Decrements the value of the number input by the `largeStep` amount.

**`Alt + ArrowUp`**
Description: Increments the value of the number input by the `smallStep` amount.

**`Alt + ArrowDown`**
Description: Decrements the value of the number input by the `smallStep` amount.

**`Home`**
Description: Sets the value of the number input to its minimum allowed value.

**`End`**
Description: Sets the value of the number input to its maximum allowed value.

**`Enter`**
Description: Submits the value entered in the number input.