Skip to Content
⭐️ Leave a star →
ComponentsNumberInput

NumberInput

Numeric input with increment / decrement buttons, min/max clamping, configurable step + precision, and full keyboard support. Controlled and uncontrolled. Use null to represent an empty input.

Features

Increment and decrement buttons clamp at min / max.
Configurable step and decimal precision.
Full keyboard support: Arrows, PageUp / PageDown, Home / End.
Commits text on blur — invalid input reverts to the last good value.
Can be controlled or uncontrolled.

Example

Value: 3

import { NumberInput } from '@wire-ui/react' <NumberInput.Root defaultValue={1} min={0} max={10}> <NumberInput.Decrement>−</NumberInput.Decrement> <NumberInput.Field aria-label="Quantity" /> <NumberInput.Increment>+</NumberInput.Increment> </NumberInput.Root>

Styling

NumberInput Root exposes data-disabled and data-readonly. Increment / Decrement buttons additionally expose data-hover, data-focus-visible, and data-active from the underlying interactive state tracker. The buttons are auto-disabled when the value reaches the relevant boundary.

<NumberInput.Root className=" data-[disabled]:opacity-50 data-[readonly]:bg-[#f5f5f5] "> <NumberInput.Decrement className=" data-[hover]:bg-[#f5f5f5] disabled:cursor-not-allowed disabled:opacity-40 ">−</NumberInput.Decrement> <NumberInput.Field /> <NumberInput.Increment>+</NumberInput.Increment> </NumberInput.Root>

Using data attributes

Root sets data-disabled and data-readonly. Increment / Decrement buttons expose interactive state attributes and the native disabled attribute when at boundary.

[data-disabled] { opacity: 0.5; } [data-readonly] { background: #f5f5f5; } button[data-hover] { background: #f5f5f5; } button:disabled { opacity: 0.4; cursor: not-allowed; }

Root props

PropTypeDefaultDescription
valuenumber | nullControlled value (null = empty)
defaultValuenumber | nullnullInitial value (uncontrolled)
onChange(value: number | null) => voidCalled when the value changes
minnumber-InfinityMinimum allowed value
maxnumberInfinityMaximum allowed value
stepnumber1Increment / decrement step
precisionnumberstep decimalsDecimal precision
disabledbooleanfalseDisables input + buttons
readOnlybooleanfalseRead-only field (buttons still disabled)

Field props

Accepts all standard <input> props except value, defaultValue, onChange, and type (the field is always rendered as type="text" with inputMode="decimal").

Increment / Decrement props

Accept all standard <button> attributes. Native disabled is automatically applied when the value is at the relevant boundary.

Data attributes

AttributeElementWhen present
data-disabledRootdisabled prop is true
data-readonlyRootreadOnly prop is true
data-hoverIncrement, DecrementMouse is over the button
data-focus-visibleIncrement, DecrementButton has keyboard focus
data-activeIncrement, DecrementButton is being pressed

Accessibility

  • Field is rendered with role="spinbutton" and aria-valuenow / aria-valuemin / aria-valuemax.
  • Increment / Decrement buttons receive aria-label="Increment" / "Decrement" and tabIndex={-1} (the field handles keyboard).
  • Field commits the typed value on blur — partial input like "-" or "." is treated as empty.

Keyboard Interactions

KeyDescription
ArrowUpIncrement by step.
ArrowDownDecrement by step.
PageUpIncrement by step * 10.
PageDownDecrement by step * 10.
HomeJump to min (when finite).
EndJump to max (when finite).
Last updated on

MIT License © 2026 wire-ui

NumberInput – Wire UI