Skip to main content

InputNumber

A numeric input field with strict digit filtering, increment/decrement controls, mouse-wheel stepping, and an optional prefix/suffix.

Unlike Input with type="number" (a native number field), InputNumber is built on a text input, so it filters the value to digits, a single dot and a leading minus, and exposes the value as number | null.

Usage

import { InputNumber } from '@easy-ui-react/easy-ui-react'

export function Example() {
const [value, setValue] = useState<number | null>(0)
return <InputNumber value={value} onValueChange={setValue} placeholder="0" />
}
Live Editor
<InputNumber defaultValue={1} placeholder="0" />
Result
Loading...

The value is number | null: null means the field is empty. Intermediate keystrokes such as 2. or - are shown while typing and reported as 2 / null respectively.

Designs

Use stepperPlacement to pick between the two layouts:

ValueDescription
endUp/down chevrons docked to the right edge (default)
sidesA button on the left and a + button on the right of the number
Live Editor
<div className={"flex gap-2"} >
    <InputNumber stepperPlacement="end" label="End" defaultValue={3} min={0} max={10} />
    <InputNumber stepperPlacement="sides" label="Sides" defaultValue={3} min={0} max={10} />
</div>
Result
Loading...

Prefix & suffix

prefix and suffix render as static text before / after the number, separated by a single space. They are never part of the editable value.

Live Editor
<div className={"flex flex-wrap gap-2"} >
    <InputNumber prefix="" defaultValue={1250} />
    <InputNumber suffix="kg" defaultValue={75} />
    <InputNumber prefix="$" suffix="USD" defaultValue={99} />
</div>
Result
Loading...

Bounds & step

min, max and step drive the increment/decrement controls and the mouse wheel. The value is clamped to [min, max], and the matching control is disabled once a bound is reached.

Live Editor
<div className={"flex gap-2"} >
    <InputNumber label="Between 0 and 10" defaultValue={5} min={0} max={10} step={1} />
    <InputNumber label="Between 0 and 100" stepperPlacement={"sides"} defaultValue={5} min={0} max={100} step={5} />
</div>
Result
Loading...

When empty, stepping starts from min (or 0 when there is no min).

Mouse wheel

While the field is focused, scrolling the mouse wheel increments or decrements the value. This is enabled by default. Set isWheelStepEnabled={false} to turn it off.

Live Editor
<InputNumber label="Scroll me while focused" defaultValue={0} step={5} />
Result
Loading...

Variants

VariantDescription
borderedFull border, transparent background
fadedFull border, tinted background
flatTinted background, no border (default)
underlinedBottom border only, no radius
Live Editor
<InputNumber variant="bordered" defaultValue={1} />
Result
Loading...

Colors

  • default
  • primary
  • secondary
  • success
  • warning
  • error
Live Editor
<InputNumber color="success" defaultValue={1} />
Result
Loading...

Sizes

  • sm
  • md
  • lg
Live Editor
<InputNumber size="lg" defaultValue={1} />
Result
Loading...

Radius

  • none
  • sm
  • md
  • lg
  • full
Live Editor
<InputNumber radius="full" defaultValue={1} />
Result
Loading...

The underlined variant always uses rounded-none regardless of the radius prop.

Content

Add an icon (or any node) before/after the number with startContent / endContent, inside or outside the box (startContentPlacement / endContentPlacement).

Live Editor
<InputNumber startContent={<Search size={16} />} defaultValue={1} />
Result
Loading...

States

Many states are supported, such as :

  • isLoading
  • isDisabled
  • isReadOnly
  • isRequired
  • isFullWidth
Live Editor
<div className={"flex flex-wrap gap-2 w-full"} >
    <InputNumber isLoading label={"Loading"} defaultValue={1} />
    <InputNumber isDisabled label={"Disabled"} defaultValue={1} />
    <InputNumber isRequired label={"Required"} defaultValue={1} />
    <div class={"flex flex-row flex-wrap lg:flex-nowrap gap-2 w-full"}>
        <InputNumber
            isReadOnly
            label={"Read only"}
            description={"Read-only hides the increment/decrement controls and the wheel stepping."}
            defaultValue={1}
        />
        <InputNumber isFullWidth label={"Full width"} defaultValue={1} />
    </div>
</div>
Result
Loading...

Label & description

Label and description are supported. You can also configure placements.

Live Editor
<div className={"flex flex-wrap gap-2"} >
    <InputNumber label="Quantity" description="How many items to order." defaultValue={1} />
    <InputNumber label="Quantity" description={"How many items to order."} descriptionPlacement={"label"} defaultValue={1} />
    <InputNumber label="Quantity" description={"How many items to order."} labelPlacement={"inside"} defaultValue={1} />
</div>
Result
Loading...

Validation

Use validations: an array of (value: number | null) => string | null functions, run on blur. null value means there is no validation error, string value represents the error message to display.

Live Editor
<InputNumber
  label="Age"
  validations={[
      v => (v !== null && v >= 18 ? null : 'Must be at least 18')
  ]}
/>
Result
Loading...

The error prop is the externally controlled error channel: use it for errors the field cannot produce on its own (a server-side response, an async check, a cross-field rule). It always takes precedence over the field's own validation (isRequired, validations), so a non-empty error is always the message shown.

Slots

SlotDescription
baseRoot <div> wrapping the whole field
labelThe <label> element
inputWrapperThe visual bordered/background box
inputThe native <input> element
prefixThe static prefix text
suffixThe static suffix text
startContentWrapper around startContent
endContentWrapper around endContent
spinnerThe loading spinner
stepperWrapper around the chevrons (stepperPlacement="end")
incrementButtonThe increment control (up chevron or + button)
decrementButtonThe decrement control (down chevron or button)
descriptionThe helper text element
errorThe error message element
Live Editor
<InputNumber classNames={{ inputWrapper: 'shadow-md', input: 'font-mono' }} defaultValue={1} />
Result
Loading...

These slots can also be styled globally for every InputNumber in your app, see the global configuration guide.

Props

PropTypeDefaultDescription
valuenumber | null-Controlled value (null is empty)
defaultValuenumber | nullnullUncontrolled initial value
onValueChange(value: number | null) => void-Called with the parsed value on every change
stepperPlacement'end' | 'sides''end'Layout of the increment/decrement controls
prefixstring-Static text before the number
suffixstring-Static text after the number
minnumber-Minimum value (clamps and disables decrement)
maxnumber-Maximum value (clamps and disables increment)
stepnumber1Increment/decrement amount
isWheelStepEnabledbooleantrueStep with the mouse wheel while focused
variant'bordered' | 'faded' | 'flat' | 'underlined''flat'Visual style
color'default' | 'primary' | 'secondary' | 'success' | 'warning' | 'error''default'Focus color
size'sm' | 'md' | 'lg''md'Field size
radius'none' | 'sm' | 'md' | 'lg' | 'full''md'Corner radius
isDisabledbooleanfalseDisables the field
isLoadingbooleanfalseShows a spinner and disables the field
isRequiredbooleanfalseMarks the field as required
isRequiredMessagestring-Message shown when a required field is left empty
isReadOnlybooleanfalseRead-only (hides the controls and wheel stepping)
isFullWidthbooleanfalseTakes the full available width
labelstring-Field label
labelPlacement'outside' | 'inside''outside'Render the label above or inside the box
descriptionstring-Helper text
descriptionPlacement'label' | 'element''element'Render description below the label or the field
errorstring-Externally controlled error, takes precedence over local validation
validationsArray<(value: number | null) => string | null>-Validators run on blur
startContentReactNode-Content rendered before the number
endContentReactNode-Content rendered after the number
startContentPlacement'inside' | 'outside''inside'Where startContent is rendered
endContentPlacement'inside' | 'outside''inside'Where endContent is rendered
classNamestring-Class applied to the root element
classNamesPartial<Record<InputNumberSlots, string>>-Per-slot class overrides

InputNumber also accepts native <input> attributes (placeholder, name, autoFocus, etc.) and forwards ref to the underlying <input> element.

Storybook

Open in Storybook → and see more combinations.