Number Input
A controlled numeric input with −/+ stepper buttons. It emits a real
number (or null when empty) — never a
string or an event — so it drops straight into
Form without parsing.
Stepper, min/max & precision
Use the buttons or type directly. ↑/↓ step by
step; hold Shift for ×10. Values clamp to
min/max on blur.
Quantity (1–99, step 1)
Price (step 0.5, 2 decimals)
Total: 9.99
import { useState } from "react";
import { NumberInput, Stack, Text } from "@usevyre/react";
const [qty, setQty] = useState<number | null>(1);
const [price, setPrice] = useState<number | null>(9.99);
<Stack direction="column" gap="lg">
<Stack direction="column" gap="xs">
<Text size="sm" color="muted">Quantity (1–99, step 1)</Text>
<NumberInput value={qty} onChange={setQty} min={1} max={99} />
</Stack>
<Stack direction="column" gap="xs">
<Text size="sm" color="muted">Price (step 0.5, 2 decimals)</Text>
<NumberInput
value={price}
onChange={setPrice}
min={0}
step={0.5}
precision={2}
/>
</Stack>
</Stack> <script setup>
import { ref } from "vue";
import { NumberInput, Stack, Text } from "@usevyre/vue";
const qty = ref(1);
const price = ref(9.99);
</script>
<template>
<Stack direction="column" gap="lg">
<Stack direction="column" gap="xs">
<Text size="sm" color="muted">Quantity (1–99, step 1)</Text>
<NumberInput v-model="qty" :min="1" :max="99" />
</Stack>
<Stack direction="column" gap="xs">
<Text size="sm" color="muted">Price (step 0.5, 2 decimals)</Text>
<NumberInput v-model="price" :min="0" :step="0.5" :precision="2" />
</Stack>
</Stack>
</template> Inside a Form
Because onChange emits a number, NumberInput
works in FormField with no glue code — the numeric value
lands in form state and validation rules like min apply
directly.
import { Form, FormField, NumberInput, Button } from "@usevyre/react";
<Form onSubmit={(v) => save(v)}>
<FormField name="age" label="Age" rules={{ required: true, min: 18 }}>
<NumberInput min={0} max={120} />
</FormField>
<Button type="submit" variant="primary">Continue</Button>
</Form> Props
Props
| Prop | Type | Default | Description |
|---|---|---|---|
value | number | null | — | Controlled value. null = empty. Omit for uncontrolled. |
defaultValue | number | null | null | Initial value when uncontrolled. |
onChange | (value: number | null) => void | — | Emits the parsed number, or null when empty. NOT an event. |
min | number | — | Minimum; clamped on blur. |
max | number | — | Maximum; clamped on blur. |
step | number | 1 | Increment/decrement amount. |
precision | number | — | Decimal places to round to. |
size | "sm" | "md" | "lg" | "md" | Control size. |
disabled | boolean | false | Disable input and steppers. |
readOnly | boolean | false | Read-only (no stepping/typing). |
Valid props
| Prop | Values | Default |
|---|---|---|
size | "sm"|"md"|"lg" | md |
disabled | true|false | false |
readOnly | true|false | false |
Common AI mistakes
- onChange={(e) => set(e.target.value)}→ onChange={(value) => set(value)} — value is number | null
- Using <Input type="number"> for numeric fields→ Use <NumberInput value onChange min max step />
- Parsing the value with Number() in form state→ Store the value directly; it is already number | null
Quick examples
Quantity selector
const [qty, setQty] = useState<number | null>(1);
<NumberInput value={qty} onChange={setQty} min={1} max={99} />Inside a Form
<FormField name="age" label="Age" rules={{ required: true, min: 18 }}>
<NumberInput min={0} max={120} />
</FormField>