Components
Component
productdevbook/ui

Stable in React

Number Field

A numeric input with increment and decrement buttons and a scrub area.

Loading React 19.3.1…
Hover a part or use it. The code follows. · Loading React…
1,397Tests passing in React · 0 failing
16.2 KiBSize
Not measured yetMemory
Not measured yetSpeed
productdevbook and 22 upstream authors7 commits · last

Install#

One package per framework. Number Field's parts come from it; the anatomy tab at the top shows how they nest.

bun add @▢▢▢▢/react

7 parts. The example uses 7 of them; the data does not say which are required.

  • NumberField.RootGroups all parts of the number field and manages its state.
  • NumberField.ScrubAreaAn interactive area where the user can click and drag to change the field value.
  • NumberField.ScrubAreaCursorA custom element to display instead of the native cursor while using the scrub area.
  • NumberField.GroupGroups the input with the increment and decrement buttons.
  • NumberField.DecrementA stepper button that decreases the field value when clicked.
  • NumberField.InputThe native input control in the number field.
  • NumberField.IncrementA stepper button that increases the field value when clicked.

Examples#

The demo at the top is the one example this component has so far.

Styling and accessibility#

The parts ship no styles. Style their states with these attributes; the CSS tab at the top has a sheet. The same CSS works in all five frameworks.

  • [data-scrubbing]Present while scrubbing. On Root, ScrubArea, ScrubAreaCursor, Group, Decrement, Input, Increment.
  • [data-disabled]Present when the number field is disabled. On Root, ScrubArea, ScrubAreaCursor, Group, Decrement, Input, Increment.
  • [data-readonly]Present when the number field is readonly. On Root, ScrubArea, ScrubAreaCursor, Group, Decrement, Input, Increment.
  • [data-required]Present when the number field is required. On Root, ScrubArea, ScrubAreaCursor, Group, Decrement, Input, Increment.
  • [data-valid]Present when the number field is in a valid state (when wrapped in Field.Root). On Root, ScrubArea, ScrubAreaCursor, Group, Decrement, Input, Increment.
  • [data-invalid]Present when the number field is in an invalid state (when wrapped in Field.Root). On Root, ScrubArea, ScrubAreaCursor, Group, Decrement, Input, Increment.
  • [data-touched]Present when the number field has been touched (when wrapped in Field.Root). On Root, ScrubArea, ScrubAreaCursor, Group, Decrement, Input, Increment.
  • [data-dirty]Present when the number field's value has changed (when wrapped in Field.Root). On Root, ScrubArea, ScrubAreaCursor, Group, Decrement, Input, Increment.
  • [data-filled]Present when the number field is filled (when wrapped in Field.Root). On Root, ScrubArea, ScrubAreaCursor, Group, Decrement, Input, Increment.
  • [data-focused]Present when the number field is focused (when wrapped in Field.Root). On Root, ScrubArea, ScrubAreaCursor, Group, Decrement, Input, Increment.

Keyboard

  • ArrowUpIncreases the value by step On Input.
  • ArrowDownDecreases the value by step On Input.
  • ShiftArrowUpShiftArrowDownSteps by largeStep On Input.
  • AltArrowUpAltArrowDownSteps by smallStep On Input.
  • HomeSets the value to min On Input.
  • EndSets the value to max On Input.
  • Lets through only what the number format accepts On Input.

Set for you

  • aria-roledescription="Number field"a screen reader says it is a number fieldInput
  • aria-labelledby to the Field label, aria-invalid when validation failsthe field is read with its name and error stateInput
  • aria-label="Increase", aria-controls to the input, tabindex="-1"the button is named and points at the input, without an extra tab stopIncrement
  • aria-label="Decrease", aria-controls to the input, tabindex="-1"the same for the other buttonDecrement
  • role="group"the input and its buttons are read as one controlGroup
  • role="presentation"the drag area is not readScrubArea

API reference#

27 props across 7 parts, in React syntax, and className, style, render once for every part. Open a row to try it.

NumberField · all parts
Shared propsEvery part that renders an element takes these.
classNameCSS class applied to the element, or a function that returns a class based on the component's state.string | ((state: State) => string | undefined)
styleStyle applied to the element, or a function that returns a style object based on the component's state.React.CSSProperties | ((state: State) => React.CSSProperties | undefined)
renderAllows you to replace the component's HTML element with a different tag, or compose it with another component.React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, State>

Use it in React#

What differs from the React API. Switch the framework at the top of the page.

@▢▢▢▢/react is the React 19 package. Its parts, props, data attributes and error messages are upstream Base UI's, and upstream's own tests run against it.

  • Parts

    Each component is its own entry point and exports one namespace named after it.

    import { Switch } from '@base-ui-next/react/switch'
    
    export function Example() {
      return (
        <Switch.Root>
          <Switch.Thumb />
        </Switch.Root>
      )
    }

    A part renders the element its API reference names, such as a <span> for Switch.Root.

  • Props and events

    Props and callbacks have upstream's names and signatures: onCheckedChange(checked, details), onOpenChange(open, details), onValueChange(value, details). details carries reason, event, cancel() and allowPropagation(). Native handlers are React's (onClick, onKeyDown), and event.preventBaseUIHandler() stops a part's own handler.

  • Styling

    className takes a string or a function (state) => string; style takes an object or a function (state) => object. The state's data-* attributes are on the element for CSS.

  • render

    render takes an element (render={<a href="/docs" />}) or a function (props, state) => element that spreads props on what it returns.

  • Refs

    ref reaches the part's element. Imperative handles are ref props, as upstream: inputRef, and actionsRef ({ validate } on Field and Form, { unmount, close } on Dialog).

  • Binding

    A part is controlled when its value prop (checked, open, value) is set, and uncontrolled with the default form (defaultChecked, defaultOpen, defaultValue). React has no two-way binding: pair the value with its change callback.

  • From @base-ui/react
    Change the import from @base-ui/react/<component> to @▢▢▢▢/react/<component>.
  • From @base-ui/react
    Nothing else changes: props, parts, data attributes and errors are the same.