Raqam
API Reference

useNumberField

Behavior hook — generates ARIA props, keyboard handlers, and event handlers for a number field.

useNumberField takes a state object from useNumberFieldState and returns prop objects (ARIA attributes, event handlers) ready to spread onto your DOM elements.

Import

import { useNumberField } from "raqam";
// or
import { useNumberField } from "raqam/react";

Signature

function useNumberField(
  props: UseNumberFieldProps,
  state: NumberFieldState,
  inputRef: React.RefObject<HTMLInputElement | null>
): NumberFieldAria;

Props

UseNumberFieldProps extends UseNumberFieldStateOptions (all state options are also accepted here for convenience) plus:

PropTypeDefaultDescription
labelstringVisible label text; used as the aria-label fallback.
incrementLabelstring"Increase"Accessible label for the increment button (incrementButtonProps); set for i18n.
decrementLabelstring"Decrease"Accessible label for the decrement button (decrementButtonProps); set for i18n.
idstringautoExplicit id for the <input>. Auto-generated via useId if omitted.
aria-labelstringAccessible label when no visible label is used.
aria-labelledbystringPoints to an external label element.
aria-describedbystringPoints to a description element. Wiring <NumberField.Description> (or any element spreading descriptionProps) is now automatic while it is mounted; a value you pass here is merged with it rather than required — see Accessibility.
namestringEnables hiddenInputProps for native form submission.
allowMouseWheelbooleanfalseEnables wheel-based increment/decrement.
copyBehavior"formatted" | "raw" | "number""formatted"What goes to the clipboard on Copy/Cut.
stepHoldDelaynumber400Milliseconds before press-and-hold acceleration starts.
stepHoldIntervalnumber200Milliseconds between accelerated steps.
onFocus(e: React.FocusEvent<HTMLInputElement>) => voidCalled when the input gains focus.
onBlur(e: React.FocusEvent<HTMLInputElement>) => voidCalled when the input loses focus (fires after commit).
onValueCommitted(value, { reason }) => voidFires when the value settles — on blur (reason: "blur") or Enter (reason: "keyboard"), after formatting + clamping.

All other props from UseNumberFieldStateOptions (minValue, maxValue, step, formatOptions, etc.) are accepted and forwarded appropriately.

Pass the same options to both hooks. useNumberField builds its own formatter and parser, so it needs the same formatting-relevant options you gave useNumberFieldState (locale, formatOptions, prefix, suffix, minValue/maxValue, allowNegative, allowDecimal, fraction-digit overrides). Share one options object between them. The NumberField components handle this wiring for you.

Return value — NumberFieldAria

KeySpread ontoDescription
labelProps<label>htmlFor wired to the input id, plus a ref that registers the label so inputProps/groupProps add aria-labelledby. Spread it as-is; don't drop the ref.
groupPropswrapping <div>role="group", aria-labelledby pointing at the label (only when a label is rendered).
inputProps<input>Full ARIA spinbutton, keyboard/wheel handlers, cursor logic.
incrementButtonPropsincrement <button>aria-label="Increase", disabled, press-and-hold.
decrementButtonPropsdecrement <button>aria-label="Decrease", disabled, press-and-hold.
hiddenInputProps<input type="hidden">value = the raw number for form submission. null when no name is set.
descriptionPropsdescription <p>A generated id, plus a ref that registers the description so the input's aria-describedby auto-wires to it while it is mounted. Spread it as-is; don't drop the ref.
errorMessagePropserror <p>role="alert", aria-live="polite". Auto-linked via the input's aria-errormessage when invalid.

Keyboard behaviour (built-in)

KeyAction
Increment by step
Decrement by step
Shift + ↑/↓Increment/decrement by largeStep
Ctrl/Cmd + ↑/↓Increment/decrement by smallStep
Page UpIncrement by largeStep
Page DownDecrement by largeStep
HomeJump to minValue
EndJump to maxValue
EnterCommit the current value (fires onValueCommitted)
BackspaceSmart deletion (deletes through grouping separators and trailing affordances)

Home/End only act when minValue/maxValue are set. For the full story on smart backspace, the smart-decimal caret jump, paste handling, and live formatting, see Formatting & Behavior.

The returned inputProps also include data-rtl, data-invalid, data-disabled, data-readonly, and data-required attributes so you can style state directly in CSS.

Mouse wheel

The wheel handler uses a non-passive event listener (bypassing React's passive-by-default onWheel) so preventDefault() can stop page scroll.

Clipboard

copyBehaviorCopy produces
"formatted" (default)Browser default — the selected text
"raw"String(numberValue) — plain ASCII digits
"number"Alias of "raw"

Example

import { useRef } from "react";
import { useNumberFieldState, useNumberField } from "raqam";

function SpinnerInput({ label }: { label: string }) {
  const ref = useRef<HTMLInputElement>(null);

  const state = useNumberFieldState({ locale: "en-US", defaultValue: 0 });

  const {
    labelProps,
    groupProps,
    inputProps,
    incrementButtonProps,
    decrementButtonProps,
  } = useNumberField({ locale: "en-US" }, state, ref);

  return (
    <div>
      <label {...labelProps}>{label}</label>
      <div {...groupProps} style={{ display: "flex" }}>
        <button {...decrementButtonProps}>−</button>
        <input {...inputProps} ref={ref} />
        <button {...incrementButtonProps}>+</button>
      </div>
    </div>
  );
}

On this page