sc1m/design
ComponentsNumber Field

Number Field

Number Field component built on Base UI, styled with sc1m tokens.

StableBase UI

Live preview

Installation

pnpm dlx shadcn@latest add @sc1m/number-field

This also merges the token layer and installs src/lib/cn.ts plus @base-ui/react. For the manual route — prerequisites, components.json, and app-level setup — see Installation.

Usage

import {
  NumberField,
  NumberFieldGroup,
  NumberFieldInput,
  NumberFieldIncrement,
  NumberFieldDecrement,
  NumberFieldScrubArea,
} from "@/components/ui/number-field";

Composition

Every part is a separate export. NumberField owns state; the rest are presentational and must appear in this order.

NumberField└── NumberFieldGroup    ├── NumberFieldDecrement    ├── NumberFieldInput    └── NumberFieldIncrement

Example

The preview above, with its source. This is the demo module verbatim, so the code and the thing it renders cannot drift apart.

<DemoNumberField />

API reference

Each part forwards every prop of its Base UI counterpart; the tables list the ones declared on the part itself. See Base UI's reference for the inherited element props.

NumberField

PropTypeDefaultDescription
idstringThe id of the input element.
minnumberThe minimum value of the input element.
maxnumberThe maximum value of the input element.
allowOutOfRangebooleanfalseWhen true, direct text entry may be outside the min/max range without clamping, so native range underflow/overflow validation can occur.
smallStepnumber0.1The small step value of the input element when incrementing while the alt key is held.
stepnumber | 'any'1Amount to increment and decrement with the buttons and arrow keys, or to scrub with pointer movement in the scrub area.
largeStepnumber10The large step value of the input element when incrementing while the shift key is held.
requiredbooleanfalseWhether the user must enter a value before submitting a form.
disabledbooleanfalseWhether the component should ignore user interaction.
readOnlybooleanfalseWhether the user should be unable to change the field value.
namestringIdentifies the field when a form is submitted.
formstringIdentifies the form that owns the hidden input.
valuenumber | nullThe raw numeric value of the field.
defaultValuenumberThe uncontrolled value of the field when it's initially rendered.
allowWheelScrubbooleanfalseWhether to allow the user to scrub the input value with the mouse wheel while focused and hovering over the input.
snapOnStepbooleanfalseWhether the value should snap to the nearest step when incrementing or decrementing.
formatIntl.NumberFormatOptionsOptions to format the input value.
onValueChange((value: number | null, eventDetails: NumberFieldRoot.ChangeEventDetails) => void)Callback fired when the number value changes.
onValueCommitted((value: number | null, eventDetails: NumberFieldRoot.CommitEventDetails) => void)Callback function that is fired when the value is committed.
localeIntl.LocalesArgumentThe locale of the input element.
inputRefReact.Ref<HTMLInputElement>A ref to access the hidden input element.

NumberFieldScrubArea

PropTypeDefaultDescription
direction'horizontal' | 'vertical''horizontal'Cursor movement direction in the scrub area.
pixelSensitivitynumber2Determines how many pixels the cursor must move before the value changes.
teleportDistancenumberIf specified, determines the distance that the cursor may move from the center of the scrub area before it will loop back around.

Accessibility

Behaviour, focus management, and ARIA wiring come from Base UI's Number Field — see its reference for the full keyboard map.

  • Disabled parts mute their contents rather than the element, keeping the focus ring at full strength where the part stays focusable.

Design tokens

The component reads semantic tokens only — no raw colour, radius, or duration appears in its source. Override the token, not the component.

TokenApplied to
--brand-radius-mdNumberFieldGroup
--fgNumberFieldInput, NumberFieldIncrement, NumberFieldDecrement
--fg-mutedNumberFieldIncrement, NumberFieldDecrement
--fg-subtleNumberFieldInput
--lineNumberFieldGroup, NumberFieldIncrement, NumberFieldDecrement
--ringNumberFieldGroup
--surfaceNumberFieldGroup, NumberFieldIncrement, NumberFieldDecrement
--surface-raisedNumberFieldGroup

Source

src/components/ui/number-field/number-field.tsx — no next/* imports, so it drops into any React 19 + Tailwind v4 app.