NumberField
A numeric input component with increment and decrement buttons, supporting scrub interaction.
Anatomy
Import and use the component standalone or composed:
1import { NumberField } from "@raystack/apsara";23{/* Standalone — renders decrement, input, and increment internally */}4<NumberField defaultValue={0} />56{/* Composed — full control over sub-components */}7<NumberField defaultValue={0}>8 <NumberField.ScrubArea label="Amount" />9 <NumberField.Group>10 <NumberField.Decrement />11 <NumberField.Input />12 <NumberField.Increment />13 </NumberField.Group>14</NumberField>
Usage
A number field bounds and steps its value. Formatting and the scrub gesture are both opt-in.
Basic
A standalone number field with default controls.
1<NumberField defaultValue={0} />
Min / max
min and max clamp the value. The stepper buttons disable at each end rather than letting the field go out of range.
1<NumberField defaultValue={5} min={0} max={10} />
Step
step sets how much the arrows and Arrow Up / Arrow Down move the value. Match it to the precision the field actually needs.
1<NumberField defaultValue={0} step={5} />
Disabled
disabled stops interaction and removes the field from the tab order, stepper buttons included.
1<NumberField defaultValue={0} disabled />
Composed with ScrubArea
Full control with scrub area for drag-to-adjust interaction.
1<NumberField defaultValue={0}>2 <NumberField.ScrubArea label="Amount" />3 <NumberField.Group>4 <NumberField.Decrement />5 <NumberField.Input />6 <NumberField.Increment />7 </NumberField.Group>8</NumberField>
Formatted
Number field with currency formatting, powered by Intl.NumberFormat internally.
format sets what to format (currency, decimals); locale sets how it's written. Pin locale when the output should be stable across different users.
1<NumberField2 defaultValue={1000}3 format={{ style: "currency", currency: "USD" }}4/>
Controlled
Pass value with onValueChange to own the number — needed when the value feeds a calculation, a query, or another field.
1(function ControlledNumberField() {2 const [qty, setQty] = React.useState(1);34 return (5 <Flex direction="column" gap={5}>6 <NumberField value={qty} onValueChange={setQty} min={1} max={10} />7 <Text size="small" variant="secondary">8 Subtotal: {qty * 12} credits9 </Text>10 </Flex>11 );12})
API Reference
A root wrapping the input and its two steppers, with ScrubArea for drag-to-change.
Root
The root component that manages numeric state. When no children are provided, it renders a default group with decrement, input, and increment controls.
Prop
Type
Group
Groups the input and button controls together.
Prop
Type
Input
The numeric input element.
Prop
Type
Decrement
Button to decrease the value.
Prop
Type
Increment
Button to increase the value.
Prop
Type
ScrubArea
An interactive area that allows adjusting the value by dragging. Renders a Label component internally.
Prop
Type
Slots
Every rendered part carries a stable data-slot attribute for styling and testing:
| Slot | Element |
|---|---|
number-field | The root container |
number-field-group | The group wrapping the input and steppers |
number-field-input | The <input> element itself |
number-field-decrement | The decrement button |
number-field-decrement-icon | The default minus icon (when no custom children) |
number-field-increment | The increment button |
number-field-increment-icon | The default plus icon (when no custom children) |
number-field-scrub-area | The NumberField.ScrubArea container |
number-field-scrub-area-label | The label inside the scrub area |
number-field-scrub-area-cursor | The virtual cursor (while scrubbing) |
number-field-scrub-area-cursor-icon | The default cursor icon (while scrubbing) |
Accessibility
- The input has
role="textbox"witharia-roledescription="Number field" - Increment and decrement buttons are keyboard accessible
- ScrubArea associates its label with the input via
htmlFor - Supports
aria-valuemin,aria-valuemax, andaria-valuenow - Keyboard: Arrow Up/Down to increment/decrement, Shift for large step, Meta for small step