Stable in React
Number Field
A numeric input with increment and decrement buttons and a scrub area.
Install#
One package per framework. Number Field's parts come from it; the anatomy tab at the top shows how they nest.
bun add @▢▢▢▢/react7 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
stepOn Input. - ArrowDownDecreases the value by
stepOn Input. - ShiftArrowUpShiftArrowDownSteps by
largeStepOn Input. - AltArrowUpAltArrowDownSteps by
smallStepOn Input. - HomeSets the value to
minOn Input. - EndSets the value to
maxOn Input. - Lets through only what the number format accepts On Input.
Set for you
a screen reader says it is a number fieldInputaria-roledescription="Number field"the field is read with its name and error stateInputaria-labelledbyto the Field label,aria-invalidwhen validation failsthe button is named and points at the input, without an extra tab stopIncrementaria-label="Increase",aria-controlsto the input,tabindex="-1"the same for the other buttonDecrementaria-label="Decrease",aria-controlsto the input,tabindex="-1"the input and its buttons are read as one controlGrouprole="group"the drag area is not readScrubArearole="presentation"
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 partsclassNameCSS 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>forSwitch.Root. - Props and events
Props and callbacks have upstream's names and signatures:
onCheckedChange(checked, details),onOpenChange(open, details),onValueChange(value, details).detailscarriesreason,event,cancel()andallowPropagation(). Native handlers are React's (onClick,onKeyDown), andevent.preventBaseUIHandler()stops a part's own handler. - Styling
classNametakes a string or a function(state) => string;styletakes an object or a function(state) => object. The state'sdata-*attributes are on the element for CSS. renderrendertakes an element (render={<a href="/docs" />}) or a function(props, state) => elementthat spreadspropson what it returns.- Refs
refreaches the part's element. Imperative handles are ref props, as upstream:inputRef, andactionsRef({ 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 thedefaultform (defaultChecked,defaultOpen,defaultValue). React has no two-way binding: pair the value with its change callback. - From @base-ui/reactChange the import from
@base-ui/react/<component>to@▢▢▢▢/react/<component>. - From @base-ui/reactNothing else changes: props, parts, data attributes and errors are the same.