Skip to main content
NumberField exposes all seven parts of the installed Base UI Number Field: Root, Group, Input, Decrement, Increment, ScrubArea, and ScrubAreaCursor. Import it from twenty-ui/primitives/input. It retains Base UI’s numeric state, formatting, stepping, event details, and native form integration with Twenty UI styling for the input, group, and adjustment controls. For the compact preset with fixed formatting and modifier-step defaults, use NumberStepper from twenty-ui/components/input.

Compose a field

Root owns numeric state. Input is the visible text input. Use Group to arrange optional decrement and increment controls around it. Supply content and accessible names for the controls; the primitive does not inject icons or labels.
Field.Label, Field.Description, and Field.Error associate with the visible input. Alternatively, pass aria-label or aria-labelledby to Input. The default aria-roledescription is Number field; it does not provide an accessible name.

Values and callbacks

Omit value for an uncontrolled field, and supply a numeric defaultValue when an initial value is needed. Omitting defaultValue starts empty. For a controlled field, pass a number or null through value and update it from onValueChange.
Clearing reports null, including when min is set. Incomplete drafts such as - remain editable without reporting an invalid number. Parseable typing updates the numeric value before the edit is committed. The first step from an empty value starts at zero clamped to the bounds; subsequent steps use the active step amount. onValueChange(value, details) retains the native event, reason, cancel, allowPropagation, isCanceled, isPropagationAllowed, trigger, and optional direction. Reasons distinguish typing (input-change), clearing (input-clear), blur normalization (input-blur), paste (input-paste), keyboard, increment or decrement presses, wheel, scrubbing, and native autofill (none). Calling cancel() prevents the numeric state update; controlled callers still choose their accepted value. onValueCommitted(value, details) runs on blur after editing, on pointer release after stepping or scrubbing, and immediately for keyboard and wheel adjustments. Clearing commits null on blur. Commit details contain event and reason; they are generic details without change cancellation methods. Focusing and blurring an untouched field does not itself commit a value when no numeric normalization is needed. Explicit rounding options can normalize and commit an untouched value.

Locale and formatting

Set locale and format on Root. They retain the installed Base UI contract: locale accepts Intl.LocalesArgument, and format accepts Intl.NumberFormatOptions. Display formatting is separate from the raw numeric value submitted by the form. The primitive does not force Latin digits, grouping, precision, or modifier steps.
Typing and paste use the configured locale and supported format symbols. Blur normalizes the displayed text. Explicit precision or rounding options can also normalize the committed number; without those options, formatting alone does not replace an untouched numeric value with rounded display text.

Stepping and bounds

step defaults to 1, smallStep to 0.1 while Alt is held, and largeStep to 10 while Shift is held. The active step applies to buttons, Arrow Up and Arrow Down, wheel, and scrub-area movement. snapOnStep defaults to false; enable it to snap adjustments to the active step. Home and End select the corresponding explicit bound. Native key handlers can call event.preventBaseUIHandler() when the owner needs to override Base UI’s keyboard behavior. min and max are inclusive. Step interactions stay bounded. By default, typed and pasted values also clamp numerically, with the draft normalized on blur. Set allowOutOfRange to accept direct edits outside those bounds and let native range validation or application validation report the problem. step="any" disables native step validation while interactive stepping uses 1 and the configured modifier steps. With a numeric step, provide min explicitly when native submission must validate step alignment. The numeric step does not round every typed value to a step.

Scrubbing

ScrubArea changes the numeric value as the pointer moves. It supports horizontal or vertical movement, pixelSensitivity, and optional teleportDistance. The active step and bounds also apply to scrubbing. Use a visible label as the scrub target while keeping the input available for keyboard editing. Compose ScrubAreaCursor inside ScrubArea for a custom cursor during pointer-lock scrubbing. It portals to the document body while active. Base UI falls back to the normal cursor for touch input, denied pointer lock, and WebKit. Set allowWheelScrub on Root to allow wheel adjustment while the input is focused and hovered; it defaults to false.

Forms and disabled state

Root renders a hidden native input type="number" for raw numeric submission and native validation. Set name, form, required, min, max, and step on Root. form can associate the field with a form elsewhere in the document. The default adjustment controls use type="button", so stepping does not submit the form. disabled prevents interaction and excludes the value from submission. readOnly prevents changes while keeping the visible input focusable and its form value available. Use a controlled value when the application must reset numeric state; native form reset does not reset it.

DOM targets and customization

Native attributes, className, style, and render target the part receiving them. Input customization applies to the visible control; wrapper customization belongs on Root or Group. To focus the control, use Input.ref, rather than Root.inputRef. Each part accepts an element or callback through render, with the corresponding public state type. Forward the supplied props and ref to the appropriate DOM element so editing, focus, labels, and validation keep working. When a stepping control renders a non-button element, set nativeButton={false}. className and style also accept state callbacks. State includes numeric value, formatted inputValue, required, disabled, readOnly, scrubbing, and inherited Field state. The public entry points export the props and state type for every part, plus NumberFieldRootChangeEventDetails, NumberFieldRootChangeEventReason, NumberFieldRootCommitEventDetails, and NumberFieldRootCommitEventReason.

Front component renderer

React and Preact sandbox fixtures verify initial locale formatting, localized paste, raw numeric form values, accessible labels, refs, render composition, blur commits, keyboard change and commit details, and pointer change details. Sequential typing after clearing a controlled localized field currently resets the caret in the renderer: typing 2 and then 3 can produce 32. A formatted pointer adjustment can also race with blur and leave the visible input showing the previous value. Pointer adjustments report changes, but pointer release does not deliver the corresponding commit callback; later blur can commit with input-blur instead. Dragging the scrub area and using its custom cursor are also unavailable in the sandbox. These limitations do not affect ordinary React hosts.

Props

The reference is generated from the installed public component types. Native attributes and event handlers are also accepted on parts that render elements.

NumberField.Root

boolean
default:"false"
Allows typed, pasted, and autofilled values outside min and max. Native range validation still applies; step interactions remain bounded.
boolean
default:"false"
Allows wheel adjustments while the visible input is focused and hovered. Defaults to false.
ReactNode
Composed NumberField parts and supporting content.
string | ((state: NumberFieldRootState) => string | undefined)
Class applied to this part, or a callback receiving its NumberField state.
number
default:"empty"
Initial uncontrolled number. Omit to start empty; the public type accepts a number.
boolean
default:"false"
Prevents interaction and excludes the value from form submission.
string
ID of the form owning the hidden native number input.
NumberFormatOptions
Intl.NumberFormat options for display and parsing, including currency, units, percent, grouping, and precision. Explicit rounding options can normalize the committed value on blur.
string
ID associated with the visible input. Generated when omitted.
Ref<HTMLInputElement>
Ref to the hidden native number input used for submission and validation. Use Input.ref to focus the visible text input.
number
default:"10"
Amount used while Shift is held. Defaults to 10.
LocalesArgument
Locale or locale list used to format and parse numbers. Defaults to the runtime locale.
number
Inclusive maximum for stepping and native range validation.
number
Inclusive minimum for stepping and native range validation.
string
Name of the raw numeric value submitted by the hidden input.
((value: number | null, eventDetails: NumberFieldRootChangeEventDetails) => void)
Receives the next number or null and change details with the native event, reason, cancellation and propagation methods, and optional stepping direction.
((value: number | null, eventDetails: NumberFieldRootCommitEventDetails) => void)
Receives the committed number or null and generic details containing event and reason. Typing commits on blur; pointer steps and scrubbing commit on release; keyboard and wheel changes commit immediately.
boolean
default:"false"
Prevents changes while keeping the visible input focusable.
Ref<HTMLDivElement>
Ref to the root div, or the element composed through Root.render.
ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, NumberFieldRootState>
Composes this part with an element or render callback. Forward the supplied props and ref to the corresponding DOM element.
boolean
default:"false"
Requires a numeric value for native form submission.
number
default:"0.1"
Amount used while Alt is held. Defaults to 0.1.
boolean
default:"false"
Snaps step interactions to multiples of the active step. Defaults to false.
number | "any"
default:"1"
Base amount for buttons, arrow keys, wheel, and scrubbing. Use any to disable native step validation while stepping by 1.
CSSProperties | ((state: NumberFieldRootState) => CSSProperties | undefined)
Inline styles applied to this part, or a callback receiving its NumberField state.
number | null
Controlled raw numeric value. Use null for an empty field.

NumberField.Group

ReactNode
The visible input and any increment or decrement controls.
string | ((state: NumberFieldGroupState) => string | undefined)
Class applied to this part, or a callback receiving its NumberField state.
Ref<HTMLDivElement>
Ref to the group div, or the element composed through Group.render.
ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, NumberFieldGroupState>
Composes this part with an element or render callback. Forward the supplied props and ref to the corresponding DOM element.
CSSProperties | ((state: NumberFieldGroupState) => CSSProperties | undefined)
Inline styles applied to this part, or a callback receiving its NumberField state.

NumberField.Input

string
default:"Number field"
Describes the role to assistive technology. Defaults to Number field; use Field.Label, aria-label, or aria-labelledby for the accessible name.
string | ((state: NumberFieldInputState) => string | undefined)
Class applied to this part, or a callback receiving its NumberField state.
Ref<HTMLInputElement>
Ref to the visible native text input.
ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<DetailedHTMLProps<InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>, NumberFieldInputState>
Composes the visible text input with an element or render callback. Forward the supplied props and ref to an input. Customize layout through Root or Group.
number
Native input size attribute. Use styles to customize visual dimensions.
CSSProperties | ((state: NumberFieldInputState) => CSSProperties | undefined)
Inline styles applied to this part, or a callback receiving its NumberField state.

NumberField.Increment

ReactNode
Visible content for the increment control. Supply its accessible name.
string | ((state: NumberFieldIncrementState) => string | undefined)
Class applied to this part, or a callback receiving its NumberField state.
boolean
default:"true"
Whether render composes a native button. Set false for a custom non-button element so Base UI supplies button interaction.
Ref<HTMLButtonElement>
Ref to the increment button, or the element composed through render.
ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, NumberFieldIncrementState>
Composes this part with an element or render callback. Forward the supplied props and ref to the corresponding DOM element.
CSSProperties | ((state: NumberFieldIncrementState) => CSSProperties | undefined)
Inline styles applied to this part, or a callback receiving its NumberField state.

NumberField.Decrement

ReactNode
Visible content for the decrement control. Supply its accessible name.
string | ((state: NumberFieldDecrementState) => string | undefined)
Class applied to this part, or a callback receiving its NumberField state.
boolean
default:"true"
Whether render composes a native button. Set false for a custom non-button element so Base UI supplies button interaction.
Ref<HTMLButtonElement>
Ref to the decrement button, or the element composed through render.
ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, NumberFieldDecrementState>
Composes this part with an element or render callback. Forward the supplied props and ref to the corresponding DOM element.
CSSProperties | ((state: NumberFieldDecrementState) => CSSProperties | undefined)
Inline styles applied to this part, or a callback receiving its NumberField state.

NumberField.ScrubArea

ReactNode
Content the user can drag to adjust the number, plus an optional ScrubAreaCursor.
string | ((state: NumberFieldScrubAreaState) => string | undefined)
Class applied to this part, or a callback receiving its NumberField state.
"horizontal" | "vertical"
default:"horizontal"
Pointer movement direction used for scrubbing.
number
default:"2"
Pixels of pointer movement needed for a step. Larger values reduce sensitivity.
Ref<HTMLSpanElement>
Ref to the scrub area span, or the element composed through render.
ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, NumberFieldScrubAreaState>
Composes this part with an element or render callback. Forward the supplied props and ref to the corresponding DOM element.
CSSProperties | ((state: NumberFieldScrubAreaState) => CSSProperties | undefined)
Inline styles applied to this part, or a callback receiving its NumberField state.
number
Optional distance from the scrub area center at which the pointer loops around.

NumberField.ScrubAreaCursor

ReactNode
Custom cursor content shown during supported pointer-lock scrubbing.
string | ((state: NumberFieldScrubAreaCursorState) => string | undefined)
Class applied to this part, or a callback receiving its NumberField state.
Ref<HTMLSpanElement>
Ref to the cursor span portaled to the document body while active. Null while absent.
ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, NumberFieldScrubAreaCursorState>
Composes this part with an element or render callback. Forward the supplied props and ref to the corresponding DOM element.
CSSProperties | ((state: NumberFieldScrubAreaCursorState) => CSSProperties | undefined)
Inline styles applied to this part, or a callback receiving its NumberField state.