moduix

Number Field

A numeric input with increment and decrement buttons, formatting, validation, and scrub controls.

API Reference

Original primitive API

Behavior, accessibility details, and low-level props are documented by Base UI.

Base UI API

Basic

import {  Field,  FieldLabel,  NumberField,} from "moduix";import { useId } from "react";export function NumberFieldDemo() {  const id = useId();  return (    <Field>      <FieldLabel htmlFor={id}>Amount</FieldLabel>      <NumberField id={id} defaultValue={100} />    </Field>  );}

Full list of component variables available for project-level overrides.

PropertyDefaultDescription
--number-field-border-colorvar(--color-border)Controls default border color.
--number-field-border-color-invalidvar(--color-destructive)Controls invalid input border and focus ring color.
--number-field-border-stylesolidControls border style for buttons and input.
--number-field-border-widthvar(--border-width-sm)Controls border width for buttons and input.
--number-field-button-bgvar(--color-background)Controls button background.
--number-field-button-bg-activevar(--number-field-button-bg-hover)Controls button background while pressed.
--number-field-button-bg-hovervar(--color-accent)Controls button background on hover.
--number-field-button-colorvar(--color-foreground)Controls button icon color.
--number-field-control-heightvar(--size-lg)Controls input and button height.
--number-field-disabled-opacityvar(--opacity-disabled)Controls disabled opacity.
--number-field-focus-ring-colorvar(--color-ring)Controls focus ring color.
--number-field-focus-ring-widthvar(--number-field-border-width)Controls focus ring width.
--number-field-gapvar(--spacing-1)Controls spacing between number field parts.
--number-field-icon-size0.875remControls button icon size.
--number-field-input-bgvar(--color-background)Controls input background.
--number-field-input-colorvar(--color-foreground)Controls input text color.
--number-field-input-font-sizevar(--text-md)Controls input font size.
--number-field-input-line-heightvar(--line-height-text-md)Controls input line height.
--number-field-input-padding-x0.75remControls input horizontal padding.
--number-field-input-padding-y0.5remControls input vertical padding.
--number-field-input-width6remControls input width.
--number-field-max-widthnoneControls the root number field max width.
--number-field-radiusvar(--radius-md)Controls the outer control corner radius.
--number-field-scrub-area-cursor-size1.5remControls custom scrub cursor size.
--number-field-scrub-area-colorvar(--color-foreground)Controls scrub area text color.
--number-field-scrub-area-gapvar(--spacing-2)Controls scrub area spacing.
--number-field-widthautoControls the root number field width.

Interactive variables scoped for docs preview without changing size scale tokens.

PropertyValueDefaultDescription
--number-field-border-colorvar(--color-border)Controls default border color.
--number-field-border-widthvar(--border-width-sm)Controls default border width.
--number-field-button-bgvar(--color-background)Controls button background.
--number-field-button-bg-hovervar(--color-accent)Controls button hover background.
--number-field-button-colorvar(--color-foreground)Controls button icon color.
--number-field-focus-ring-colorvar(--color-ring)Controls focus ring color.
--number-field-input-bgvar(--color-background)Controls input background.
--number-field-input-colorvar(--color-foreground)Controls input text color.
--number-field-radiusvar(--radius-md)Controls control corner radius.

Anatomy

NumberField keeps numeric state on the root and renders the standard decrement, input, and increment group automatically. Add NumberFieldScrubArea only when the label or another visible part should support drag-to-change behavior.

Field
├─ FieldLabel
└─ NumberField
   ├─ NumberFieldScrubArea
   │  ├─ scrub label
   │  └─ cursor (internal)
   └─ NumberFieldGroup
      ├─ NumberFieldDecrement
      ├─ NumberFieldInput
      └─ NumberFieldIncrement
<Field>
  <FieldLabel htmlFor={id}>Amount</FieldLabel>
  <NumberField id={id} defaultValue={100} />
</Field>
PartRole
NumberFieldRoot state machine. Controls value, formatting, validation, min/max, step, and change handlers.
NumberFieldScrubAreaOptional drag area for changing the value. It renders the scrub cursor internally by default.
NumberFieldGroupVisual wrapper for the stepper buttons and input. Rendered automatically unless withGroup={false} is set.
NumberFieldDecrementButton that decreases the value. It renders the default minus icon, or your custom icon as children.
NumberFieldInputEditable numeric input. It receives formatted text and invalid, disabled, and readonly state attributes.
NumberFieldIncrementButton that increases the value. It renders the default plus icon, or your custom icon as children.

NumberField does not use portal-like service layers such as portal, backdrop, or viewport. The standard group is rendered internally by default and can be styled with classNames.group, classNames.decrement, classNames.input, and classNames.increment. The scrub cursor is an internal service slot of NumberFieldScrubArea, so it is customized through cursor, withCursor, and classNames.cursor instead of being added to the public composition.

Composition

Use NumberField for root behavior props such as value, defaultValue, onValueChange, min, max, step, required, disabled, readOnly, and format.

Use the default group for standard number inputs. Set decrementLabel and incrementLabel to localize the internal button labels, or set withGroup={false} and compose NumberFieldGroup, NumberFieldDecrement, NumberFieldInput, and NumberFieldIncrement manually when the layout or icons need full control.

Visible parts accept className: NumberField, NumberFieldScrubArea, NumberFieldGroup, NumberFieldDecrement, NumberFieldInput, and NumberFieldIncrement. NumberFieldScrubArea also accepts cursor for replacing the default scrub cursor icon, withCursor={false} for hiding it, and classNames.cursor for styling the internal cursor wrapper.

Examples

Controlled

Control the numeric value from React state when other UI needs to react to the current number.

Current value: 24
import {  Field,  FieldLabel,  NumberField,} from "moduix";import { useState, useId } from "react";export function ControlledNumberFieldDemo() {  const id = useId();  const [value, setValue] = useState(24 as number | null);  return (    <div>      <Field>        <FieldLabel htmlFor={id}>Controlled value</FieldLabel>        <NumberField id={id} value={value} onValueChange={setValue} />      </Field>      <span>Current value: {value ?? "empty"}</span>    </div>  );}

Min, Max, and Step

Use min, max, and step to constrain button, keyboard, scrub, and enabled wheel-scrub interactions.

import {  Field,  FieldLabel,  NumberField,} from "moduix";import { useId } from "react";export function MinMaxStepNumberFieldDemo() {  const id = useId();  return (    <Field>      <FieldLabel htmlFor={id}>Quantity (0-20, step 2)</FieldLabel>      <NumberField id={id} defaultValue={10} min={0} max={20} step={2} />    </Field>  );}

Formatting

Use format for localized presentation while keeping the value numeric.

import {  Field,  FieldLabel,  NumberField,} from "moduix";import { useId } from "react";export function FormattedNumberFieldDemo() {  const id = useId();  return (    <Field>      <FieldLabel htmlFor={id}>Price</FieldLabel>      <NumberField        id={id}        defaultValue={1250}        min={0}        step={50}        format={{ style: "currency", currency: "USD", maximumFractionDigits: 0 }}      />    </Field>  );}

Scrub Area

Add NumberFieldScrubArea when users should be able to drag the label area to change the value. The scrub cursor renders automatically and can be customized with the cursor, withCursor, and classNames.cursor props.

import {  NumberField,  NumberFieldScrubArea,  FieldLabel,} from "moduix";import { useId } from "react";export function ScrubAreaNumberFieldDemo() {  const id = useId();  return (    <NumberField id={id} defaultValue={250}>      <NumberFieldScrubArea>        <FieldLabel htmlFor={id}>Drag to scrub</FieldLabel>      </NumberFieldScrubArea>    </NumberField>  );}

Field Validation

Wrap the control in Field to show native validation errors from required, min, and max.

import {  Field,  FieldLabel,  NumberField,  FieldError,} from "moduix";import { useId } from "react";export function NumberFieldValidationDemo() {  const id = useId();  return (    <Field name="quantity" validationMode="onBlur">      <FieldLabel htmlFor={id}>Items</FieldLabel>      <NumberField id={id} min={1} max={10} required />      <FieldError match="valueMissing">Please provide a number.</FieldError>      <FieldError match="rangeUnderflow">Value should be at least 1.</FieldError>      <FieldError match="rangeOverflow">Value should be at most 10.</FieldError>    </Field>  );}

Custom Icons

Pass children to the stepper buttons to use icons from your application or icon library.

import {  Field,  FieldLabel,  NumberField,  NumberFieldGroup,  NumberFieldDecrement,  ChevronDownIcon,  NumberFieldInput,  NumberFieldIncrement,  ChevronUpIcon,} from "moduix";import { useId } from "react";import styles from "./number-field.module.css";export function CustomIconsNumberFieldDemo() {  const id = useId();  return (    <Field>      <FieldLabel htmlFor={id}>Floors</FieldLabel>      <NumberField id={id} defaultValue={8} withGroup={false}>        <NumberFieldGroup>          <NumberFieldDecrement aria-label="Decrease value" className={styles.customButton}>            <ChevronDownIcon />          </NumberFieldDecrement>          <NumberFieldInput className={styles.customInput} />          <NumberFieldIncrement aria-label="Increase value" className={styles.customButton}>            <ChevronUpIcon />          </NumberFieldIncrement>        </NumberFieldGroup>      </NumberField>    </Field>  );}
.customButton {  --number-field-button-bg: var(--color-muted);  --number-field-button-bg-hover: var(--color-accent);  --number-field-icon-size: 1rem;}.customInput {  --number-field-input-width: 7rem;  --number-field-input-font-size: var(--text-lg);}

On this page