Numeric input with spin controls, clamp behaviors, precision, prefix/suffix, and thousands-separator formatting.
- Spinbutton with ArrowUp/ArrowDown step bumps and clickable increment/decrement controls
- Configurable clamp behavior (strict, on-blur, or none) with min/max bounds
- Precision rounding avoids floating-point artifacts (e.g. 0.1 + 0.2)
- Optional prefix, suffix, and thousands-separator with parse-aware display
- Surface/tone aware; controlled —
value + onChange (React), v-model (Vue), [(value)] or forms (Angular) — or uncontrolled with defaultValue
- Status
- stable
- Category
- forms
- Since
- v1.8.0
import { PixelNumberInput } from '@pxlkit/ui-kit';
| Prop | Type | Default | Description |
|---|
value | number | — | Value; leave unset for an uncontrolled field. |
defaultValue | number | — | Initial value while uncontrolled. |
onChange | (next: number) => void | — | Called with the new number, after each step, edit that reads as a number, or settle on blur. |
min | number | — | Lowest value. |
max | number | — | Highest value. |
step | number | 1 | Amount each step adds or removes. |
precision | number | — | Decimals shown, and the value is rounded to. |
clampBehavior | 'strict' | 'blur' | 'none' | 'blur' | When a value outside min / max is pulled back: while typing, on blur, or never. |
prefix | string | — | Text inside the field on the left ($). |
suffix | string | — | Text inside the field on the right (USD). |
thousandsSeparator | string | — | Groups the integer digits (, shows 1,500,000). |
allowNegative | boolean | true | Accepts negative numbers. |
hideControls | boolean | false | Hides the stepper buttons. |
size | 'sm' | 'md' | 'lg' | 'md' | Field height. |
surface | 'pixel' | 'linear' | — | Surface override; defaults to the nearest provider. |
tone | 'green' | 'cyan' | 'gold' | 'red' | 'purple' | 'pink' | 'neutral' | 'neutral' | Tone of the focus ring. |
label | string | — | Label rendered above the field. |
hint | string | — | Helper text below the field; hidden while error is set. |
error | string | — | Error message below the field; marks the input invalid. |
Also takes the native attributes and event handlers of <input> (InputHTMLAttributes<HTMLInputElement>), except type.
ref points to <input>.
WCAG target: 2.1 AA
Renders role="spinbutton" with aria-valuemin/aria-valuemax/aria-valuenow reflecting the current numeric state. Error state sets aria-invalid.
Keyboard
| Key | Action | When |
|---|
| ArrowUp | Increment value by step | — |
| ArrowDown | Decrement value by step | — |
import { useState } from 'react';
import { PixelNumberInput } from '@pxlkit/ui-kit';
export function Default() {
const [value, setValue] = useState<number>(5);
return (
<PixelNumberInput
label="Quantity"
value={value}
onChange={setValue}
min={0}
max={100}
/>
);
}
Examples
Default
import { useState } from 'react';
import { PixelNumberInput } from '@pxlkit/ui-kit';
export function Default() {
const [value, setValue] = useState<number>(5);
return (
<PixelNumberInput
label="Quantity"
value={value}
onChange={setValue}
min={0}
max={100}
/>
);
}
With Prefix & Suffix
import { useState } from 'react';
import { PixelNumberInput } from '@pxlkit/ui-kit';
export function WithPrefixSuffix() {
const [value, setValue] = useState<number>(19.99);
return (
<PixelNumberInput
label="Price"
value={value}
onChange={setValue}
prefix="$"
suffix="USD"
precision={2}
step={0.01}
min={0}
/>
);
}
Thousands Separator
import { useState } from 'react';
import { PixelNumberInput } from '@pxlkit/ui-kit';
export function ThousandsSeparator() {
const [value, setValue] = useState<number>(1500000);
return (
<PixelNumberInput
label="Population"
value={value}
onChange={setValue}
thousandsSeparator=","
min={0}
/>
);
}
Hide Controls
import { useState } from 'react';
import { PixelNumberInput } from '@pxlkit/ui-kit';
export function HideControls() {
const [value, setValue] = useState<number>(42);
return (
<PixelNumberInput
label="Age"
value={value}
onChange={setValue}
hideControls
min={0}
max={120}
/>
);
}
With Error
import { useState } from 'react';
import { PixelNumberInput } from '@pxlkit/ui-kit';
export function WithError() {
const [value, setValue] = useState<number>(150);
return (
<PixelNumberInput
label="Score"
value={value}
onChange={setValue}
min={0}
max={100}
error="Score must be between 0 and 100"
tone="red"
/>
);
}