Number Input
A field that allows user input of numeric values.
You can explore the number-input component in the following curated examples.
Anatomy
To set up the number input correctly, you'll need to understand its anatomy and how we name its parts.
Each part includes a
data-partattribute to help identify them in the DOM.
Examples
Learn how to use the NumberInput component in your project. Let's take a look at the most basic example:
import { NumberInput } from '@ark-ui/react/number-input'
export const Basic = () => (
<NumberInput.Root>
<NumberInput.Label>Label</NumberInput.Label>
<NumberInput.Input />
<NumberInput.Control>
<NumberInput.DecrementTrigger>-1</NumberInput.DecrementTrigger>
<NumberInput.IncrementTrigger>+1</NumberInput.IncrementTrigger>
</NumberInput.Control>
</NumberInput.Root>
)
import { NumberInput } from '@ark-ui/solid/number-input'
export const Basic = () => (
<NumberInput.Root>
<NumberInput.Label>Label</NumberInput.Label>
<NumberInput.Input />
<NumberInput.Control>
<NumberInput.DecrementTrigger>-1</NumberInput.DecrementTrigger>
<NumberInput.IncrementTrigger>+1</NumberInput.IncrementTrigger>
</NumberInput.Control>
</NumberInput.Root>
)
<script setup lang="ts">
import { NumberInput } from '@ark-ui/vue/number-input'
</script>
<template>
<NumberInput.Root>
<NumberInput.Label>Label</NumberInput.Label>
<NumberInput.Input />
<NumberInput.Control>
<NumberInput.DecrementTrigger>-1</NumberInput.DecrementTrigger>
<NumberInput.IncrementTrigger>+1</NumberInput.IncrementTrigger>
</NumberInput.Control>
</NumberInput.Root>
</template>
<script lang="ts">
import { NumberInput } from '@ark-ui/svelte/number-input'
</script>
<NumberInput.Root>
<NumberInput.Label>Count</NumberInput.Label>
<NumberInput.Control>
<NumberInput.DecrementTrigger>-</NumberInput.DecrementTrigger>
<NumberInput.Input />
<NumberInput.IncrementTrigger>+</NumberInput.IncrementTrigger>
</NumberInput.Control>
</NumberInput.Root>
Setting a minimum and maximum value
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.
import { NumberInput } from '@ark-ui/react/number-input'
export const MinMax = () => (
<NumberInput.Root min={0} max={10}>
<NumberInput.Scrubber />
<NumberInput.Label>Label</NumberInput.Label>
<NumberInput.Input />
<NumberInput.Control>
<NumberInput.DecrementTrigger>-1</NumberInput.DecrementTrigger>
<NumberInput.IncrementTrigger>+1</NumberInput.IncrementTrigger>
</NumberInput.Control>
</NumberInput.Root>
)
import { NumberInput } from '@ark-ui/solid/number-input'
export const MinMax = () => (
<NumberInput.Root min={0} max={10}>
<NumberInput.Scrubber />
<NumberInput.Label>Label</NumberInput.Label>
<NumberInput.Input />
<NumberInput.Control>
<NumberInput.DecrementTrigger>-1</NumberInput.DecrementTrigger>
<NumberInput.IncrementTrigger>+1</NumberInput.IncrementTrigger>
</NumberInput.Control>
</NumberInput.Root>
)
<script setup lang="ts">
import { NumberInput } from '@ark-ui/vue/number-input'
</script>
<template>
<NumberInput.Root :min="0" :max="10">
<NumberInput.Scrubber />
<NumberInput.Label>Label</NumberInput.Label>
<NumberInput.Input />
<NumberInput.Control>
<NumberInput.DecrementTrigger>-1</NumberInput.DecrementTrigger>
<NumberInput.IncrementTrigger>+1</NumberInput.IncrementTrigger>
</NumberInput.Control>
</NumberInput.Root>
</template>
<script lang="ts">
import { NumberInput } from '@ark-ui/svelte/number-input'
</script>
<NumberInput.Root min={0} max={10}>
<NumberInput.Scrubber />
<NumberInput.Label>Label</NumberInput.Label>
<NumberInput.Input />
<NumberInput.Control>
<NumberInput.DecrementTrigger>-1</NumberInput.DecrementTrigger>
<NumberInput.IncrementTrigger>+1</NumberInput.IncrementTrigger>
</NumberInput.Control>
</NumberInput.Root>
Adjusting the precision of the value
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.
import { NumberInput } from '@ark-ui/react/number-input'
export const FractionDigits = () => (
<NumberInput.Root formatOptions={{ minimumFractionDigits: 2, maximumFractionDigits: 3 }} defaultValue="1.00">
<NumberInput.Scrubber />
<NumberInput.Label>Label</NumberInput.Label>
<NumberInput.Input />
<NumberInput.Control>
<NumberInput.DecrementTrigger>-1</NumberInput.DecrementTrigger>
<NumberInput.IncrementTrigger>+1</NumberInput.IncrementTrigger>
</NumberInput.Control>
</NumberInput.Root>
)
import { NumberInput } from '@ark-ui/solid/number-input'
export const FractionDigits = () => (
<NumberInput.Root formatOptions={{ minimumFractionDigits: 2, maximumFractionDigits: 4 }} value="1.00">
<NumberInput.Scrubber />
<NumberInput.Label>Label</NumberInput.Label>
<NumberInput.Input />
<NumberInput.Control>
<NumberInput.DecrementTrigger>-1</NumberInput.DecrementTrigger>
<NumberInput.IncrementTrigger>+1</NumberInput.IncrementTrigger>
</NumberInput.Control>
</NumberInput.Root>
)
<script setup lang="ts">
import { NumberInput } from '@ark-ui/vue/number-input'
</script>
<template>
<NumberInput.Root :formatOptions="{ minimumFractionDigits: 2, maximumFractionDigits: 3 }" model-value="1.00">
<NumberInput.Scrubber />
<NumberInput.Label>Label</NumberInput.Label>
<NumberInput.Input />
<NumberInput.Control>
<NumberInput.DecrementTrigger>-1</NumberInput.DecrementTrigger>
<NumberInput.IncrementTrigger>+1</NumberInput.IncrementTrigger>
</NumberInput.Control>
</NumberInput.Root>
</template>
<script lang="ts">
import { NumberInput } from '@ark-ui/svelte/number-input'
</script>
<NumberInput.Root formatOptions={{ minimumFractionDigits: 2, maximumFractionDigits: 3 }} defaultValue="1.00">
<NumberInput.Scrubber />
<NumberInput.Label>Label</NumberInput.Label>
<NumberInput.Input />
<NumberInput.Control>
<NumberInput.DecrementTrigger>-1</NumberInput.DecrementTrigger>
<NumberInput.IncrementTrigger>+1</NumberInput.IncrementTrigger>
</NumberInput.Control>
</NumberInput.Root>
Scrubbing the input value
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.
import { NumberInput } from '@ark-ui/react/number-input'
export const Scrubber = () => (
<NumberInput.Root>
<NumberInput.Scrubber />
<NumberInput.Label>Label</NumberInput.Label>
<NumberInput.Input />
<NumberInput.Control>
<NumberInput.DecrementTrigger>-1</NumberInput.DecrementTrigger>
<NumberInput.IncrementTrigger>+1</NumberInput.IncrementTrigger>
</NumberInput.Control>
</NumberInput.Root>
)
import { NumberInput } from '@ark-ui/solid/number-input'
export const Scrubber = () => (
<NumberInput.Root>
<NumberInput.Scrubber />
<NumberInput.Label>Label</NumberInput.Label>
<NumberInput.Input />
<NumberInput.Control>
<NumberInput.DecrementTrigger>-1</NumberInput.DecrementTrigger>
<NumberInput.IncrementTrigger>+1</NumberInput.IncrementTrigger>
</NumberInput.Control>
</NumberInput.Root>
)
<script setup lang="ts">
import { NumberInput } from '@ark-ui/vue/number-input'
</script>
<template>
<NumberInput.Root>
<NumberInput.Scrubber />
<NumberInput.Label>Label</NumberInput.Label>
<NumberInput.Input />
<NumberInput.Control>
<NumberInput.DecrementTrigger>-1</NumberInput.DecrementTrigger>
<NumberInput.IncrementTrigger>+1</NumberInput.IncrementTrigger>
</NumberInput.Control>
</NumberInput.Root>
</template>
<script lang="ts">
import { NumberInput } from '@ark-ui/svelte/number-input'
</script>
<NumberInput.Root>
<NumberInput.Scrubber />
<NumberInput.Label>Label</NumberInput.Label>
<NumberInput.Input />
<NumberInput.Control>
<NumberInput.DecrementTrigger>-1</NumberInput.DecrementTrigger>
<NumberInput.IncrementTrigger>+1</NumberInput.IncrementTrigger>
</NumberInput.Control>
</NumberInput.Root>
Using the mouse wheel to change value
The NumberInput exposes a way to increment/decrement the value using the mouse wheel event. To activate this, set the
allowMouseWheel prop to true.
import { NumberInput } from '@ark-ui/react/number-input'
export const MouseWheel = () => (
<NumberInput.Root allowMouseWheel>
<NumberInput.Scrubber />
<NumberInput.Label>Label</NumberInput.Label>
<NumberInput.Input />
<NumberInput.Control>
<NumberInput.DecrementTrigger>-1</NumberInput.DecrementTrigger>
<NumberInput.IncrementTrigger>+1</NumberInput.IncrementTrigger>
</NumberInput.Control>
</NumberInput.Root>
)
import { NumberInput } from '@ark-ui/solid/number-input'
export const MouseWheel = () => (
<NumberInput.Root allowMouseWheel>
<NumberInput.Scrubber />
<NumberInput.Label>Label</NumberInput.Label>
<NumberInput.Input />
<NumberInput.Control>
<NumberInput.DecrementTrigger>-1</NumberInput.DecrementTrigger>
<NumberInput.IncrementTrigger>+1</NumberInput.IncrementTrigger>
</NumberInput.Control>
</NumberInput.Root>
)
<script setup lang="ts">
import { NumberInput } from '@ark-ui/vue/number-input'
</script>
<template>
<NumberInput.Root allowMouseWheel>
<NumberInput.Scrubber />
<NumberInput.Label>Label</NumberInput.Label>
<NumberInput.Input />
<NumberInput.Control>
<NumberInput.DecrementTrigger>-1</NumberInput.DecrementTrigger>
<NumberInput.IncrementTrigger>+1</NumberInput.IncrementTrigger>
</NumberInput.Control>
</NumberInput.Root>
</template>
<script lang="ts">
import { NumberInput } from '@ark-ui/svelte/number-input'
</script>
<NumberInput.Root allowMouseWheel>
<NumberInput.Scrubber />
<NumberInput.Label>Label</NumberInput.Label>
<NumberInput.Input />
<NumberInput.Control>
<NumberInput.DecrementTrigger>-1</NumberInput.DecrementTrigger>
<NumberInput.IncrementTrigger>+1</NumberInput.IncrementTrigger>
</NumberInput.Control>
</NumberInput.Root>
Clamp value when user blurs the input
In most cases, users can type custom values in the input field. If the typed value is greater than the max, the value is reset to max when the user blur out of the input.
To disable this behavior, pass clampValueOnBlur and set to false.
import { NumberInput } from '@ark-ui/react/number-input'
export const NoClamp = () => (
<NumberInput.Root clampValueOnBlur={false}>
<NumberInput.Scrubber />
<NumberInput.Label>Label</NumberInput.Label>
<NumberInput.Input />
<NumberInput.Control>
<NumberInput.DecrementTrigger>-1</NumberInput.DecrementTrigger>
<NumberInput.IncrementTrigger>+1</NumberInput.IncrementTrigger>
</NumberInput.Control>
</NumberInput.Root>
)
import { NumberInput } from '@ark-ui/solid/number-input'
export const NoClamp = () => (
<NumberInput.Root clampValueOnBlur={false}>
<NumberInput.Scrubber />
<NumberInput.Label>Label</NumberInput.Label>
<NumberInput.Input />
<NumberInput.Control>
<NumberInput.DecrementTrigger>-1</NumberInput.DecrementTrigger>
<NumberInput.IncrementTrigger>+1</NumberInput.IncrementTrigger>
</NumberInput.Control>
</NumberInput.Root>
)
<script setup lang="ts">
import { NumberInput } from '@ark-ui/vue/number-input'
</script>
<template>
<NumberInput.Root :clampValueOnBlur="false">
<NumberInput.Scrubber />
<NumberInput.Label>Label</NumberInput.Label>
<NumberInput.Input />
<NumberInput.Control>
<NumberInput.DecrementTrigger>-1</NumberInput.DecrementTrigger>
<NumberInput.IncrementTrigger>+1</NumberInput.IncrementTrigger>
</NumberInput.Control>
</NumberInput.Root>
</template>
<script lang="ts">
import { NumberInput } from '@ark-ui/svelte/number-input'
</script>
<NumberInput.Root clampValueOnBlur={false}>
<NumberInput.Scrubber />
<NumberInput.Label>Label</NumberInput.Label>
<NumberInput.Input />
<NumberInput.Control>
<NumberInput.DecrementTrigger>-1</NumberInput.DecrementTrigger>
<NumberInput.IncrementTrigger>+1</NumberInput.IncrementTrigger>
</NumberInput.Control>
</NumberInput.Root>
Usage within forms
To use the number input within forms, set the name prop.
import { NumberInput } from '@ark-ui/react/number-input'
export const FormUsage = () => (
<NumberInput.Root name="quantity">
<NumberInput.Scrubber />
<NumberInput.Label>Label</NumberInput.Label>
<NumberInput.Input />
<NumberInput.Control>
<NumberInput.DecrementTrigger>-1</NumberInput.DecrementTrigger>
<NumberInput.IncrementTrigger>+1</NumberInput.IncrementTrigger>
</NumberInput.Control>
</NumberInput.Root>
)
import { NumberInput } from '@ark-ui/solid/number-input'
export const FormUsage = () => (
<NumberInput.Root name="quantity">
<NumberInput.Scrubber />
<NumberInput.Label>Label</NumberInput.Label>
<NumberInput.Input />
<NumberInput.Control>
<NumberInput.DecrementTrigger>-1</NumberInput.DecrementTrigger>
<NumberInput.IncrementTrigger>+1</NumberInput.IncrementTrigger>
</NumberInput.Control>
</NumberInput.Root>
)
<script setup lang="ts">
import { NumberInput } from '@ark-ui/vue/number-input'
</script>
<template>
<NumberInput.Root name="quantity">
<NumberInput.Scrubber />
<NumberInput.Label>Label</NumberInput.Label>
<NumberInput.Input />
<NumberInput.Control>
<NumberInput.DecrementTrigger>-1</NumberInput.DecrementTrigger>
<NumberInput.IncrementTrigger>+1</NumberInput.IncrementTrigger>
</NumberInput.Control>
</NumberInput.Root>
</template>
<script lang="ts">
import { NumberInput } from '@ark-ui/svelte/number-input'
</script>
<NumberInput.Root name="quantity">
<NumberInput.Scrubber />
<NumberInput.Label>Label</NumberInput.Label>
<NumberInput.Input />
<NumberInput.Control>
<NumberInput.DecrementTrigger>-1</NumberInput.DecrementTrigger>
<NumberInput.IncrementTrigger>+1</NumberInput.IncrementTrigger>
</NumberInput.Control>
</NumberInput.Root>
Format and parse value
To apply custom formatting to the input's value, set the formatOptions and provide Intl.NumberFormatOptions such as
style and currency.
import { NumberInput } from '@ark-ui/react/number-input'
export const Formatted = () => (
<NumberInput.Root
formatOptions={{
style: 'currency',
currency: 'USD',
}}
>
<NumberInput.Scrubber />
<NumberInput.Label>Label</NumberInput.Label>
<NumberInput.Input />
<NumberInput.Control>
<NumberInput.DecrementTrigger>-1</NumberInput.DecrementTrigger>
<NumberInput.IncrementTrigger>+1</NumberInput.IncrementTrigger>
</NumberInput.Control>
</NumberInput.Root>
)
import { NumberInput } from '@ark-ui/solid/number-input'
export const Formatted = () => (
<NumberInput.Root
formatOptions={{
style: 'currency',
currency: 'USD',
}}
>
<NumberInput.Scrubber />
<NumberInput.Label>Label</NumberInput.Label>
<NumberInput.Input />
<NumberInput.Control>
<NumberInput.DecrementTrigger>-1</NumberInput.DecrementTrigger>
<NumberInput.IncrementTrigger>+1</NumberInput.IncrementTrigger>
</NumberInput.Control>
</NumberInput.Root>
)
<script setup lang="ts">
import { NumberInput } from '@ark-ui/vue/number-input'
</script>
<template>
<NumberInput.Root :formatOptions="{ style: 'currency', currency: 'USD' }">
<NumberInput.Scrubber />
<NumberInput.Label>Label</NumberInput.Label>
<NumberInput.Input />
<NumberInput.Control>
<NumberInput.DecrementTrigger>-1</NumberInput.DecrementTrigger>
<NumberInput.IncrementTrigger>+1</NumberInput.IncrementTrigger>
</NumberInput.Control>
</NumberInput.Root>
</template>
<script lang="ts">
import { NumberInput } from '@ark-ui/svelte/number-input'
</script>
<NumberInput.Root
formatOptions={{
style: 'currency',
currency: 'USD',
}}
>
<NumberInput.Scrubber />
<NumberInput.Label>Label</NumberInput.Label>
<NumberInput.Input />
<NumberInput.Control>
<NumberInput.DecrementTrigger>-1</NumberInput.DecrementTrigger>
<NumberInput.IncrementTrigger>+1</NumberInput.IncrementTrigger>
</NumberInput.Control>
</NumberInput.Root>
Using the Field Component
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.
import { Field } from '@ark-ui/react/field'
import { NumberInput } from '@ark-ui/react/number-input'
export const WithField = (props: Field.RootProps) => (
<Field.Root {...props}>
<NumberInput.Root>
<NumberInput.Label>Label</NumberInput.Label>
<NumberInput.Input />
<NumberInput.Control>
<NumberInput.DecrementTrigger>-1</NumberInput.DecrementTrigger>
<NumberInput.IncrementTrigger>+1</NumberInput.IncrementTrigger>
</NumberInput.Control>
</NumberInput.Root>
<Field.HelperText>Additional Info</Field.HelperText>
<Field.ErrorText>Error Info</Field.ErrorText>
</Field.Root>
)
import { Field } from '@ark-ui/solid/field'
import { NumberInput } from '@ark-ui/solid/number-input'
export const WithField = (props: Field.RootProps) => (
<Field.Root {...props}>
<NumberInput.Root>
<NumberInput.Label>Label</NumberInput.Label>
<NumberInput.Input />
<NumberInput.Control>
<NumberInput.DecrementTrigger>-1</NumberInput.DecrementTrigger>
<NumberInput.IncrementTrigger>+1</NumberInput.IncrementTrigger>
</NumberInput.Control>
</NumberInput.Root>
<Field.HelperText>Additional Info</Field.HelperText>
<Field.ErrorText>Error Info</Field.ErrorText>
</Field.Root>
)
<script setup lang="ts">
import { Field, type FieldRootProps } from '@ark-ui/vue/field'
import { NumberInput } from '@ark-ui/vue/number-input'
const props = defineProps<FieldRootProps>()
</script>
<template>
<Field.Root v-bind="props">
<NumberInput.Root>
<NumberInput.Label>Label</NumberInput.Label>
<NumberInput.Input />
<NumberInput.Control>
<NumberInput.DecrementTrigger>-1</NumberInput.DecrementTrigger>
<NumberInput.IncrementTrigger>+1</NumberInput.IncrementTrigger>
</NumberInput.Control>
</NumberInput.Root>
<Field.HelperText>Additional Info</Field.HelperText>
<Field.ErrorText>Error Info</Field.ErrorText>
</Field.Root>
</template>
<script lang="ts">
import { Field } from '@ark-ui/svelte/field'
import { NumberInput } from '@ark-ui/svelte/number-input'
</script>
<Field.Root>
<NumberInput.Root>
<NumberInput.Label>Label</NumberInput.Label>
<NumberInput.Input />
<NumberInput.Control>
<NumberInput.DecrementTrigger>-1</NumberInput.DecrementTrigger>
<NumberInput.IncrementTrigger>+1</NumberInput.IncrementTrigger>
</NumberInput.Control>
</NumberInput.Root>
<Field.HelperText>Additional Info</Field.HelperText>
<Field.ErrorText>Error Info</Field.ErrorText>
</Field.Root>
Using the Root Provider
The RootProvider component provides a context for the number-input. It accepts the value of the useNumber-input
hook. You can leverage it to access the component state and methods from outside the number-input.
import { NumberInput, useNumberInput } from '@ark-ui/react/number-input'
export const RootProvider = () => {
const numberInput = useNumberInput()
return (
<>
<button onClick={() => numberInput.focus()}>Focus</button>
<NumberInput.RootProvider value={numberInput}>
<NumberInput.Label>Label</NumberInput.Label>
<NumberInput.Input />
<NumberInput.Control>
<NumberInput.DecrementTrigger>-1</NumberInput.DecrementTrigger>
<NumberInput.IncrementTrigger>+1</NumberInput.IncrementTrigger>
</NumberInput.Control>
</NumberInput.RootProvider>
</>
)
}
import { NumberInput, useNumberInput } from '@ark-ui/solid/number-input'
export const RootProvider = () => {
const numberInput = useNumberInput()
return (
<>
<button onClick={() => numberInput().focus()}>Focus</button>
<NumberInput.RootProvider value={numberInput}>
<NumberInput.Label>Label</NumberInput.Label>
<NumberInput.Input />
<NumberInput.Control>
<NumberInput.DecrementTrigger>-1</NumberInput.DecrementTrigger>
<NumberInput.IncrementTrigger>+1</NumberInput.IncrementTrigger>
</NumberInput.Control>
</NumberInput.RootProvider>
</>
)
}
<script setup lang="ts">
import { NumberInput, useNumberInput } from '@ark-ui/vue/number-input'
const numberInput = useNumberInput()
</script>
<template>
<button @click="numberInput.focus()">Focus</button>
<NumberInput.RootProvider :value="numberInput">
<NumberInput.Label>Label</NumberInput.Label>
<NumberInput.Input />
<NumberInput.Control>
<NumberInput.DecrementTrigger>-1</NumberInput.DecrementTrigger>
<NumberInput.IncrementTrigger>+1</NumberInput.IncrementTrigger>
</NumberInput.Control>
</NumberInput.RootProvider>
</template>
<script lang="ts">
import { NumberInput, useNumberInput } from '@ark-ui/svelte/number-input'
const id = $props.id()
const numberInput = useNumberInput({ id })
</script>
<NumberInput.RootProvider value={numberInput}>
<NumberInput.Scrubber />
<NumberInput.Label>Count</NumberInput.Label>
<NumberInput.Control>
<NumberInput.DecrementTrigger>-</NumberInput.DecrementTrigger>
<NumberInput.Input />
<NumberInput.IncrementTrigger>+</NumberInput.IncrementTrigger>
</NumberInput.Control>
</NumberInput.RootProvider>
If you're using the
RootProvidercomponent, you don't need to use theRootcomponent.
Guides
Using the 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 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.
Controlling the value
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.
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:
<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
Root
| Prop | Default | Type |
|---|---|---|
allowMouseWheel | booleanWhether to allow mouse wheel to change the value | |
allowOverflow | true | booleanWhether to allow the value overflow the min/max range |
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. | |
clampValueOnBlur | true | booleanWhether to clamp the value when the input loses focus (blur) |
defaultValue | stringThe initial value of the input when rendered. Use when you don't need to control the value of the input. | |
disabled | booleanWhether the number input is disabled. | |
focusInputOnChange | true | booleanWhether to focus input when the value changes |
form | stringThe associate form of the input element. | |
formatOptions | NumberFormatOptionsThe options to pass to the `Intl.NumberFormat` constructor | |
id | stringThe unique identifier of the machine. | |
ids | Partial<{
root: string
label: string
input: string
incrementTrigger: string
decrementTrigger: string
scrubber: string
}>The ids of the elements in the number input. Useful for composition. | |
inputMode | 'decimal' | InputModeHints 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 | booleanWhether the number input value is invalid. | |
locale | 'en-US' | stringThe current locale. Based on the BCP 47 definition. |
max | Number.MAX_SAFE_INTEGER | numberThe maximum value of the number input |
min | Number.MIN_SAFE_INTEGER | numberThe minimum value of the number input |
name | stringThe name attribute of the number input. Useful for form submission. | |
onFocusChange | (details: FocusChangeDetails) => voidFunction invoked when the number input is focused | |
onValueChange | (details: ValueChangeDetails) => voidFunction invoked when the value changes | |
onValueInvalid | (details: ValueInvalidDetails) => voidFunction invoked when the value overflows or underflows the min/max range | |
pattern | '-?[0-9]*(.[0-9]+)?' | stringThe pattern used to check the <input> element's value against |
readOnly | booleanWhether the number input is readonly | |
required | booleanWhether the number input is required | |
spinOnPress | true | booleanWhether to spin the value when the increment/decrement button is pressed |
step | 1 | numberThe amount to increment or decrement the value by |
translations | IntlTranslationsSpecifies the localized strings that identifies the accessibility elements and their states | |
value | stringThe controlled value of the input |
| Data Attribute | Value |
|---|---|
[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
| Prop | Default | Type |
|---|---|---|
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
| Data Attribute | Value |
|---|---|
[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
| Prop | Default | Type |
|---|---|---|
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
| Data Attribute | Value |
|---|---|
[data-scope] | number-input |
[data-part] | decrement-trigger |
[data-disabled] | Present when disabled |
[data-scrubbing] |
IncrementTrigger
| Prop | Default | Type |
|---|---|---|
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
| Data Attribute | Value |
|---|---|
[data-scope] | number-input |
[data-part] | increment-trigger |
[data-disabled] | Present when disabled |
[data-scrubbing] |
Input
| Prop | Default | Type |
|---|---|---|
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
| Data Attribute | Value |
|---|---|
[data-scope] | number-input |
[data-part] | input |
[data-invalid] | Present when invalid |
[data-disabled] | Present when disabled |
[data-scrubbing] |
Label
| Prop | Default | Type |
|---|---|---|
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
| Data Attribute | Value |
|---|---|
[data-scope] | number-input |
[data-part] | label |
[data-disabled] | Present when disabled |
[data-focus] | Present when focused |
[data-invalid] | Present when invalid |
[data-scrubbing] |
RootProvider
| Prop | Default | Type |
|---|---|---|
value | UseNumberInputReturn | |
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
Scrubber
| Prop | Default | Type |
|---|---|---|
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
| Data Attribute | Value |
|---|---|
[data-scope] | number-input |
[data-part] | scrubber |
[data-disabled] | Present when disabled |
[data-scrubbing] |
ValueText
| Prop | Default | Type |
|---|---|---|
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
| Data Attribute | Value |
|---|---|
[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] |
Accessibility
Complies with the Spinbutton WAI-ARIA design pattern.
Keyboard Support
| Key | Description |
|---|---|
ArrowUp | Increments the value of the number input by a predefined step. |
ArrowDown | Decrements the value of the number input by a predefined step. |
PageUp | Increments the value of the number input by a larger predefined step. |
PageDown | Decrements the value of the number input by a larger predefined step. |
Home | Sets the value of the number input to its minimum allowed value. |
End | Sets the value of the number input to its maximum allowed value. |
Enter | Submits the value entered in the number input. |