- Accordion
- Avatar
- Badge
- Breadcrumb
- Button
- Calendar
- Checkbox
- Combobox
- Container
- CurrencyInput
- DistributionSlider
- Drawer
- Dropdown
- Grid
- Heading
- Image
- Input
- InputGroup
- Label
- Logo
- MapPin
- Modal
- NativeSelect
- NumberInput
- OtpInput
- PhoneInput
- Popover
- Progress
- PropertyCalendar
- RadioGroup
- RadioGroupCards
- ResponsiveModal
- ScrollArea
- SearchBar
- SearchBarFallback
- SearchInput
- Select
- Separator
- Spinner
- Switch
- Tabs
- Text
- Textarea
- Toast
- Toggle
- ToggleGroup
- Tooltip
NumberInput
A simple counter component with increment and decrement buttons
pnpm add @wandercom/design-system-web
import { NumberInput } from '@wandercom/design-system-web/ui/number-input';
export function Example() {
const [value, setValue] = useState<number | null>(5);
return (
<NumberInput
value={value}
min={0}
max={10}
step={1}
onChange={setValue}
/>
);
}Basic number input with increment and decrement buttons.
Number input displaying a placeholder when value is null.
const [value, setValue] = useState<number | null>(null);
<NumberInput
value={value}
min={0}
max={100}
step={5}
placeholder="Any"
onChange={setValue}
/>Number input with currency symbol prefix.
const [value, setValue] = useState<number | null>(50);
<NumberInput
value={value}
min={0}
max={500}
step={25}
currency="USD"
onChange={setValue}
/>Number input with a custom placeholder text.
const [value, setValue] = useState<number | null>(null);
<NumberInput
value={value}
min={1}
max={10}
step={1}
placeholder="None"
onChange={setValue}
/>Number input demonstrating various currency and locale combinations with proper formatting:
- United States (en-US):
$1,500(comma separator, symbol before) - Germany (de-DE):
1.500 €(period separator, symbol after) - United Kingdom (en-GB):
£1,500(comma separator, symbol before) - Japan (ja-JP):
¥1,500(comma separator, symbol before)
import { Label } from '@wandercom/design-system-web/ui/label';
const [usValue, setUsValue] = useState<number | null>(1500);
const [deValue, setDeValue] = useState<number | null>(1500);
const [gbValue, setGbValue] = useState<number | null>(1500);
const [jpValue, setJpValue] = useState<number | null>(1500);
<div className="flex flex-col gap-4">
<div className="flex flex-col gap-2">
<Label htmlFor="us-input">United States (en-US)</Label>
<NumberInput
id="us-input"
value={usValue}
min={0}
max={10000}
step={100}
currency="USD"
locale="en-US"
onChange={setUsValue}
/>
</div>
<div className="flex flex-col gap-2">
<Label htmlFor="de-input">Germany (de-DE)</Label>
<NumberInput
id="de-input"
value={deValue}
min={0}
max={10000}
step={100}
currency="EUR"
locale="de-DE"
onChange={setDeValue}
/>
</div>
<div className="flex flex-col gap-2">
<Label htmlFor="gb-input">United Kingdom (en-GB)</Label>
<NumberInput
id="gb-input"
value={gbValue}
min={0}
max={10000}
step={100}
currency="GBP"
locale="en-GB"
onChange={setGbValue}
/>
</div>
<div className="flex flex-col gap-2">
<Label htmlFor="jp-input">Japan (ja-JP)</Label>
<NumberInput
id="jp-input"
value={jpValue}
min={0}
max={10000}
step={100}
currency="JPY"
locale="ja-JP"
onChange={setJpValue}
/>
</div>
</div>value?:
min?:
max?:
step?:
placeholder?:
currency?:
locale?:
onChange?:
onFocus?:
onBlur?:
disabled?:
required?:
aria-label?:
aria-labelledby?:
aria-describedby?:
id?:
name?:
className?:
This component follows Radix UI patterns and WAI-ARIA Spinbutton specification:
Keyboard Navigation:
Enter/Space- Enter edit mode for manual inputArrow Up/Arrow Down- Increment/decrement by stepPage Up/Page Down- Increment/decrement by step × 10Home/End- Jump to min/max valuesTab- Move focus to/from the spinbuttonEscape- Exit edit mode without saving (when editing)
Screen Reader Support:
- Uses
role="spinbutton"with proper ARIA attributes - Announces current value, min, and max to screen readers
- Buttons are excluded from tab order (only spinbutton is focusable)
- Currency symbol is hidden from screen readers via
aria-hidden
Form Integration:
- Optional hidden input for native form submission when
nameprop is provided - Supports
requiredanddisabledstates
Increment/decrement buttons adjust the value by the step amount, respecting min and max bounds. When the value is null (placeholder state), pressing increment sets the value to min rather than min + step.
Click the value or press Enter/Space to enter edit mode for manual keyboard input.
Manual input supports direct number entry. Press Enter to confirm or Escape to cancel.
Buttons are disabled when the value reaches the minimum or maximum limit.
Placeholder display shows custom text when value is null, useful for "Any" or "None" states.
Values are automatically clamped to stay within the min and max range.
International formatting uses Intl.NumberFormat to display numbers with locale-specific thousands separators and currency symbols.
Focus management ensures the spinbutton receives focus for keyboard navigation while buttons remain clickable.