eoria
Component

Stepper

Number input as minus, value and plus, with press-and-hold repeat and screen-reader adjust actions.

Stepper rendered on iOS, light mode
Stepper rendered on iOS, dark mode
iPhone, zinc preset. Captured from the example app.
Terminal window
npx @eoria/cli add stepper

Usage

import { useState } from 'react'
import { Stepper } from '@/components/ui/stepper'
export function Quantity() {
const [quantity, setQuantity] = useState(1)
return (
<Stepper
value={quantity}
onValueChange={setQuantity}
min={1}
max={10}
accessibilityLabel="Quantity"
/>
)
}
<Stepper size="sm" defaultValue={0.5} min={0} max={5} step={0.5} formatValue={(n) => `${n} kg`} />

Props

Prop Values Default
value number; controlled
defaultValue number min
min, max number 0, 100
step amount one press moves 1
onValueChange (value: number) => void
formatValue (value: number) => string, shown and announced String
size sm, md, the heights of theme.control md
disabled boolean false
styles override root, rootDisabled, button, buttonPressed, buttonDisabled, glyph, value

Notes

A press moves the value by one step. Holding a button for 400 ms starts a repeat at 160 ms that shortens by 15% per tick down to 40 ms. It stops on release and at a bound. A button at its bound dims and stops taking presses.

The value is set in tabular figures with a minimum width, so the buttons hold still while the digits change. Steps such as 0.1 are rounded to the decimals of step and min, so 0.1 plus 0.2 shows 0.3.

For screen readers the whole control is one adjustable element. It reports formatValue(value) as its value and answers the increment and decrement actions by one step. The two buttons are for touch only. Give the Stepper an accessibilityLabel. The defaults of 0 to 100 follow UIStepper.

The minus and plus are drawn with bars from the glyph slot, so the file has no icon dependency. Buttons under 44pt get hitSlop upwards, downwards and outwards.

Source

registry/ui/stepper.tsx