Number Input
A number field with steppers. Uses type="text" with inputMode="decimal", because the native number input silently discards what it cannot parse — a half-typed "1e" or "-" vanishes as you type it.
src/components/ui/number-input.tsxAbout
From the component's own source, where it was written.
A number field with steppers.
inputMode="decimal" with type="text", not type="number". The native number input silently discards what it cannot parse, so a half-typed "1e" or "-" vanishes as you type it; it also exposes a spinner that cannot be styled and scrolls the value on wheel over the field. Keeping the text input means the caller sees every keystroke and the value is parsed on commit.
Clamping happens on blur rather than on each keystroke, so typing "5" on the way to "50" in a field with a minimum of 10 is not rewritten under the cursor.
Install
npx astralyx-ui add number-input
Usage
import { NumberInput } from '@/components/ui/number-input'
<NumberInput value={qty} onValueChange={setQty} min={1} max={99} suffix="items" />Composer
Change the props and the source below follows.
<NumberInput
defaultValue={12}
min={0}
max={99}
step={1}
size="md"
suffix="items"
/>Examples
Sizes and affixes
<NumberInput defaultValue={12} min={0} max={99} />
<NumberInput defaultValue={9.5} step={0.5} prefix="$" />API
Generated from the source, so it cannot fall behind the code. One table per exported component, then hooks, functions and types.
NumberInput
A number field with steppers.
Extends Omit<ComponentProps<'input'>, 'value' | 'defaultValue' | 'onChange' | 'size' | 'type' | 'prefix'>.
| Prop | Type | Default | Description |
|---|---|---|---|
| max | number | string | — | Clamped on blur, not per keystroke — rewriting "5" to "10" mid-type fights someone entering "50". |
| min | number | string | — | Clamped on blur, not per keystroke — rewriting "5" to "10" mid-type fights someone entering "50". |
| step | number | string | 1 | Precision is inferred from the step, so 0.1 stays at one decimal instead of accumulating float noise. |
| value | number | — | Reports undefined while the field is empty or unparseable, rather than coercing to 0. |
| defaultValue | number | — | Reports undefined while the field is empty or unparseable, rather than coercing to 0. |
| onValueChange | (value: number) => void | — | Reports undefined while the field is empty or unparseable, rather than coercing to 0. |
| precision | number | 1 | Precision is inferred from the step, so 0.1 stays at one decimal instead of accumulating float noise. |
| size | keyof typeof fieldSize | 'md' | — |
| variant | 'default' | 'secondary' | 'ghost' | 'default' | — |
| error | boolean | false | — |
| prefix | string | — | Static affixes inside the field. |
| suffix | string | — | Static affixes inside the field. |
Behaviour
| Topic | Detail | Description |
|---|---|---|
| keyboard | ↑ ↓ | Arrow keys step. The steppers are out of the tab order, since the field itself already does this. |