Skip to content

Number Input

Enter or step through a number, formatted for your locale, currency or unit.

import { NumberInput } from '@youj/react'

Installation & usage

npm i @youj/react

For an app already set up with npx @youj/cli init, which installs the peers (@youj/core, @youj/motion, motion and Base UI) and wires the provider. New app? Start at Installation.

Use a number input when the answer is a quantity people adjust: a budget, a replica count, a timeout, a discount. It parses what they type in the locale it formats in, clamps it to min and max, and formats it back with Intl.NumberFormat. For a value picked along a range by feel, use a Slider. For digits that aren't a quantity, such as a verification code or a phone number, use Input OTP or Phone Input.

import { useState } from 'react'
import { Field, FieldLabel, NumberInput } from '@youj/react'

export function Replicas() {
  const [replicas, setReplicas] = useState<number | null>(3)
  return (
    <Field>
      <FieldLabel>Replicas</FieldLabel>
      <NumberInput value={replicas} onValueChange={setReplicas} min={1} max={20} />
    </Field>
  )
}

The value is a number, or null when the field is empty. It's built on Base UI's NumberField and rendered as an Input Group, so it takes a Field's label, description, error and required with no extra wiring.

Reference

Propsvalue, defaultValue, onValueChange… (23)

Props

NumberInput props
PropType
valuenumber | null–
defaultValuenumber | null–
onValueChange(value: number | null) => void–
minnumber–
maxnumber–
stepnumber

Per ArrowUp/Down and button press.

default 1

1
largeStepnumber

Per Shift+ArrowUp/Down.

default 10

10
formatIntl.NumberFormatOptions

Intl.NumberFormat options: { style: 'currency', currency: 'USD' }, { style: 'percent' }, { style: 'unit', unit: 'gigabyte' }…

–
localeIntl.LocalesArgument

Default: the YoujProvider locale.

–
unitReactNode

A plain suffix addon ("GB", "req/s").

–
prefixReactNode

A plain prefix addon.

–
allowWheelScrubboolean

Let the mouse wheel step the value while the field is focused.

default false

false
steppersboolean

Shows the − / + buttons.

default true

true
placeholderstring–
disabledboolean–
readOnlyboolean–
requiredboolean–
namestring–
idstring–
refRef<HTMLInputElement>–
onBlur() => void–
classNamestring–
aria-labelstring–
MotionStep, stepper press · reduced motion

Motion

Motion tokens
MomentTokens
Stepthe number nudges 3px from the direction it moved: up for more, down for less
duration.fastease.enter

140ms · cubic-bezier(0.16, 1, 0.3, 1)

140mscubic-bezier(0.16, 1, 0.3, 1)
Stepper pressthe − and + buttons press like every Button
duration.instantease.standard

80ms · cubic-bezier(0.2, 0, 0, 1)

80mscubic-bezier(0.2, 0, 0, 1)

Reduced motion

The number changes in place with no nudge.

A held button fires a step on every repeat, and each one restarts the nudge, so a fast count reads as movement rather than a flicker. Typing a value never nudges: only steps from the keyboard, the buttons or the wheel do.

Reduced motion

Steps change the number in place, with no nudge. Formatting, clamping and press-and-hold are unchanged.

AccessibilityKeyboard, ARIA, announcements

Accessibility

  • It's Base UI's NumberField: a text input described as a number field, inside a group, so screen readers hear the formatted value as it changes.
  • ArrowUp and ArrowDown step by step, Shift with an arrow by largeStep, and Home and End go to the bounds.
  • The steppers are named "Decrease" and "Increase". They stay out of the tab order, because the arrow keys already do their job from the input.
  • Name it with a Field's label, or with aria-label on its own. A unit addon is visible text, so the label doesn't need to repeat it.