Number Input

Numeric field with parse / clamp, optional percentage suffix, and optional steppers. Composed from Input Group, so it reads identically to every other form field.

£
<NumberInput defaultValue={1500} prefix="£" />

Usage

import { NumberInput } from "@reva/ui";

NumberInput owns numeric parsing — value and onValueChange speak number | null (null when the field is empty or not yet a number). It does not wrap a native type="number"; it composes Input Group so the border, hover, focus / invalid rings, radius, and size ladder are identical to Input.

const [amount, setAmount] = useState<number | null>(null);

<NumberInput onValueChange={setAmount} prefix="£" value={amount} />;

Type freely — intermediate drafts like - or 1. are preserved while you edit. On blur the value is clamped to min / max and rounded to precision.

Examples

Controlled value

onValueChange fires on every edit and on blur with the parsed number | null.

£

Value: 1500

"use client";import { NumberInput, Text, VStack } from "@reva/ui";import { useState } from "react";export function AmountField() {const [value, setValue] = useState<number | null>(1500);return (  <VStack className="w-full max-w-xs" gap={2}>    <NumberInput      aria-label="Amount"      onValueChange={setValue}      placeholder="0"      prefix="£"      value={value}    />    <Text color="muted-foreground" size="sm">      Value: {value === null ? "null" : value}    </Text>  </VStack>);}

Percentage

Set percent for a trailing % suffix. It is visual only3 means 3%, and the value stays 3. Convert to a fraction at your API boundary if the server expects 0.03.

%

Value: 3.5

<NumberInput max={100} min={0} percent precision={2} value={value} onValueChange={setValue} />

Steppers

Set showSteppers for / + buttons. They (and the Arrow Up / Arrow Down keys) step by step and clamp to min / max.

Value: 2

<NumberInput max={10} min={0} showSteppers step={1} value={value} onValueChange={setValue} />

Percentage with steppers

percent and showSteppers compose — the % suffix sits alongside the trailing stepper.

%
<NumberInput max={100} min={0} percent precision={2} showSteppers step={0.25} value={value} onValueChange={setValue} />

Sizes

Set size: sm (32px height), default (40px), lg (44px), xl (48px) — the same ladder as Input.

%
%
%
%
<VStack className="w-full max-w-sm" gap={3}>  <NumberInput defaultValue={42} percent size="sm" />  <NumberInput defaultValue={42} percent size="default" />  <NumberInput defaultValue={42} percent size="lg" />  <NumberInput defaultValue={42} percent size="xl" /></VStack>

Disabled

%
<NumberInput disabled percent value={5} />

Invalid

Use aria-invalid for error styling (matches the shared form-control error ring).

%
<NumberInput aria-invalid percent value={150} />

Props

PropDescription
valueControlled value, number | null. null = empty / not-a-number.
defaultValueUncontrolled initial value, number | null.
onValueChange(value: number | null) => void — fires on every edit and on blur.
min / maxOptional bounds; clamped on blur and on each step.
stepIncrement / decrement for steppers and arrow keys. Default 1.
precisionDecimal places to round to on blur.
percentRender a trailing % suffix (visual only — 3 = 3%).
showSteppersShow / + stepper buttons.
prefix / suffixLeading / trailing add-on content (e.g. prefix="£"). percent is sugar for suffix="%".
sizesm | default | lg | xl — control height, padding, and font size. Default default.
Other props (name, placeholder, disabled, aria-*, …) forward to the underlying input.

On this page