|Kit

Number Input

A numeric input with increment / decrement steppers on Ark UI's NumberInput machine — clamping, locale-aware formatting, mouse-wheel, and the full keyboard model on the shared input shell.

NumberInput is a numeric <input> with spin buttons, built on Ark UI's NumberInput machine and the same shell as Input. It clamps to a range, formats with Intl.NumberFormat, supports the mouse wheel, and ships the full keyboard model (↑ / ↓ step, PageUp / PageDown jump, Home / End to min / max).

import { NumberInput } from '@42-central/ui-react/number-input';

<NumberInput defaultValue={3} min={0} max={10} />

The value is a real number (or null for empty) — the machine speaks strings internally, but value / defaultValue take a number | null and onChange hands one back, null whenever the field has no valid number. Like the rest of the family it carries its own label, description, error, and required — no external wrapper needed — and passing error flips it to the invalid (red) state. Omit them and it still composes cleanly inside a Field.

Playground

Component

Loading preview

Props

Code

<Component
  label="Slots"
  size="md"
  step={1}
/>

Min, max & step

min / max clamp the range; step sets the spin amount. With allowMouseWheel, scrolling over a focused input nudges the value too.

Formatting

formatOptions is forwarded straight to Intl.NumberFormat, so currency, percent, and unit display come for free — the parsed value stays a plain number.

Sizes

Five heights — xs through xl — matching the rest of the family. The control font, padding, and the steppers scale to match.

Code

<NumberInput size="xs" defaultValue={1} />
<NumberInput size="sm" defaultValue={1} />
<NumberInput size="md" defaultValue={1} />
<NumberInput size="lg" defaultValue={1} />
<NumberInput size="xl" defaultValue={1} />

No controls

hideControls drops the steppers for a plain numeric field — useful inside dense forms where the spin buttons would crowd the layout.

Clearable

clearable adds a clear (×) button once there's a value — the numeric analogue of Select's clearable. Clicking it resets the field to null. placeholder shows while the field is empty.

Colors

Like the rest of the form family, the input is palette-independent throughout — including the focus halo, which is a fixed brand-500 per the 42 UI Kit spec. color is accepted for consistency with the rest of the kit but tints nothing here. See Colors and Focus.

Code

<NumberInput color="brand" defaultValue={1} />
<NumberInput color="green" defaultValue={1} />
<NumberInput color="purple" defaultValue={1} />

Invalid & disabled

invalid swaps the border and ring to red regardless of color (and sets aria-invalid). disabled dims the shell and blocks input; readOnly keeps full contrast but rejects edits.

Code

<NumberInput invalid defaultValue={5} />
<NumberInput disabled defaultValue={5} />
<NumberInput readOnly defaultValue={5} />

With label, description & error

label, description (helper text), and error are built in via Field — no external wrapper needed. Passing error flips the shell to the invalid (red) state; required adds the semantic flag and a red asterisk on the label.

How many evaluation slots to book.
Pick at least one

Code

<NumberInput label="Slots" description="How many evaluation slots to book." defaultValue={1} min={1} />

<NumberInput label="Slots" required defaultValue={1} min={1} />

<NumberInput label="Slots" error="Pick at least one" defaultValue={0} min={1} />

If you omit these props, the control still composes inside an outer Field, inheriting the field's invalid, disabled, and readOnly from context — so an error on the field flips the input red automatically.

Controlled

Drive the value from state. onChange hands you a number, or null while the field is empty or mid-edit.

'use client';
import { useState } from 'react';
import { NumberInput } from '@42-central/ui-react/number-input';

export function Slots() {
  const [value, setValue] = useState<number | null>(1);
  return <NumberInput value={value} onChange={setValue} clearable min={1} max={99} />;
}

Forms

A name wires the underlying input into native <form> submission and FormData — no controlled state required.

<form action={save}>
  <NumberInput name="slots" defaultValue={1} min={1} />
</form>

Translations

translations reuses Ark's own NumberInput keys — the stepper names and valueText, which sets the input's aria-valuetext — plus the combobox family's clearTriggerLabel. Unset keys keep their English default.

<NumberInput
  clearable
  translations={{
    incrementLabel: 'Augmenter',
    decrementLabel: 'Diminuer',
    clearTriggerLabel: 'Effacer',
    valueText: (value) => `${value} crédits`,
  }}
/>

API

Prop

Type

Accessibility

  • Full NumberInput keyboard model from Ark: ↑ / ↓ step, PageUp / PageDown jump by a larger step, Home / End snap to min / max.
  • The input is role="spinbutton" with aria-valuemin / aria-valuemax / aria-valuenow / aria-valuetext wired from the machine.
  • invalid sets aria-invalid. Pass label / description / error for an accessible name and error text via the built-in Field, or compose inside an outer Field instead.
  • The steppers are real <button>s with Increment / Decrement labels; the wheel and scrub gestures are progressive enhancements over keyboard / typing.
  • suffix is decorative only — aria-hidden and non-selectable, never part of the editable value or the accessible name. To show a compact unit hint without a visible label, keep passing label (it stays the accessible name) and visually hide it with classNames={{ label: "sr-only" }}.

Changelog

v0.19.0
  • A translations prop for the built-in trigger, clear, remove, search, increment/decrement and upload labels

    #331
v0.16.0
  • Wrap around only when stepping, not when typing

    #250
v0.6.0
  • Inputs drop the filled variant and the color prop

    #96
v0.5.1
  • Field's label names the real control through aria-labelledby

    #77
v0.5.0
  • Wire the field's description and error to the control with aria-describedby and aria-errormessage

    #57
v0.3.0
  • The suffix sits right against the value

    #23
v0.2.0
  • wrap rolls the value over at min/max instead of clamping

    #16
v0.0.1
  • Textarea, PasswordInput, NumberInput and PinInput

On this page