Dots

Number Field

A number input with minus and plus buttons, limits, steps and locale-aware formatting.

From 1 to 12.
"use client"

import {

NumberField is a number input on React Aria NumberField, with a minus and a plus button around the value. It parses and formats by locale, keeps the value inside minValue and maxValue, snaps to step, and turns a button off at its limit. Use it for counts and small limits, such as the number of payments, cards to order or a per-payment limit in steps of $50. For an amount of money typed freely, use AmountField instead.

Installation

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

Usage

import {
  NumberField,
  NumberFieldDecrement,
  NumberFieldDescription,
  NumberFieldGroup,
  NumberFieldIncrement,
  NumberFieldInput,
  NumberFieldLabel,
} from "@/components/ui/number-field"
<NumberField defaultValue={4} minValue={1} maxValue={12}>
  <NumberFieldLabel>Number of payments</NumberFieldLabel>
  <NumberFieldGroup>
    <NumberFieldDecrement />
    <NumberFieldInput />
    <NumberFieldIncrement />
  </NumberFieldGroup>
  <NumberFieldDescription>From 1 to 12.</NumberFieldDescription>
</NumberField>

Composition

Use the following composition to build a number field:

NumberField
├── NumberFieldLabel
├── NumberFieldGroup
│   ├── NumberFieldDecrement
│   ├── NumberFieldInput
│   └── NumberFieldIncrement
├── NumberFieldDescription
└── NumberFieldError

Features

  • Locale-aware parsing and formatting, including currency, percent and units through formatOptions
  • minValue, maxValue and step, with typed values snapped to the step on blur
  • Minus and plus buttons that turn off at the limits
  • Arrow keys and Page Up/Page Down step by step, and Home and End jump to minValue and maxValue when set
  • Buttons stay out of the Tab order, and tapping them does not open the phone keyboard
  • Label, description and error wired to the input by React Aria
  • Controlled and uncontrolled use

Currency

Pass formatOptions with style: "currency" to show the value as money. Combine it with step for limits that move in fixed amounts.

$50 to $5,000. Typed amounts snap to $50.
"use client"

import * as React from "react"
<NumberField
  value={limit}
  onChange={setLimit}
  minValue={50}
  maxValue={5000}
  step={50}
  formatOptions={{ style: "currency", currency: "USD", maximumFractionDigits: 0 }}
>
  {/* label, group, description */}
</NumberField>

Unit

Use style: "unit" for counts with a unit, such as weeks between repeat payments.

"use client"

import {

Invalid

Set isInvalid from your own rule and render NumberFieldError to say what to fix. minValue and maxValue clamp the value instead of flagging it, so leave maxValue off when you want to show an error for a value that is too high, as this demo does.

You can order up to 10 cards at once.
"use client"

import * as React from "react"

Disabled

Set isDisabled to turn off the input and both buttons. Keep a description that says why.

Set by the schedule owner.
"use client"

import {

Accessibility

React Aria renders the input with role="textbox" and an aria-roledescription of "Number field" (except on iOS), links it to NumberFieldLabel, NumberFieldDescription and NumberFieldError, and gives the buttons localized names such as "Decrease" and "Increase" that include the field label.

  • Always render NumberFieldLabel, or pass aria-label to NumberField when there is no visible label.
  • Put the limits in NumberFieldDescription, so they are read with the field.
  • Say how to fix the value in NumberFieldError, not only that it is wrong.
  • The buttons are left out of the Tab order, so keyboard users step with the arrow keys from the input. Screen reader users can still reach the buttons by swiping.

API Reference

NumberField

The root. Wraps React Aria NumberField and accepts all of its props. The most used ones are listed here.

PropTypeDefaultDescription
valuenumber-The value when controlled.
defaultValuenumber-The starting value when uncontrolled.
onChange(value: number) => void-Called when the value is committed: on step, blur or Enter.
minValuenumber-The smallest allowed value.
maxValuenumber-The largest allowed value.
stepnumber1The amount the buttons and arrow keys change the value by.
formatOptionsIntl.NumberFormatOptions-How the value is formatted and parsed, e.g. currency or unit.
isDisabledboolean-Turns off the input and buttons.
isReadOnlyboolean-The value can be read and copied but not changed.
isRequiredboolean-A value is required before the form submits.
isInvalidboolean-Shows the invalid state and NumberFieldError.
validationBehavior"native" | "aria""native"aria shows errors in real time instead of on submit.
namestring-The name of the value in a form submit.
classNamestring-Additional classes to apply to the root.

NumberFieldLabel

The field label. Renders a Label and accepts all of its props.

PropTypeDefaultDescription
classNamestring-Additional classes to apply to the label.

NumberFieldGroup

The surface that holds the buttons and the input. It is the kit's InputGroup, on React Aria Group, and takes the disabled and invalid state from the field.

PropTypeDefaultDescription
classNamestring-Additional classes to apply to the group.

NumberFieldInput

The number itself, centered between the buttons with tabular digits. Renders an InputGroupInput and accepts all of its props. Pass ref to focus it.

PropTypeDefaultDescription
classNamestring-Additional classes to apply to the input.

NumberFieldDecrement

Lowers the value by step. Renders an InputGroupButton with a minus icon in a start addon. Its accessible name comes from React Aria.

PropTypeDefaultDescription
classNamestring-Additional classes to apply to the button.
...propsReact.ComponentProps<typeof InputGroupButton>-Button props, except slot, children, size and variant.

NumberFieldIncrement

Raises the value by step. Renders an InputGroupButton with a plus icon in an end addon. Its accessible name comes from React Aria.

PropTypeDefaultDescription
classNamestring-Additional classes to apply to the button.
...propsReact.ComponentProps<typeof InputGroupButton>-Button props, except slot, children, size and variant.

NumberFieldDescription

Help under the field, such as the limits. Renders React Aria Text with slot="description".

PropTypeDefaultDescription
classNamestring-Additional classes to apply to the description.

NumberFieldError

Says what to fix. Renders React Aria FieldError and shows only while the field is invalid.

PropTypeDefaultDescription
childrenReactNode | (validation: ValidationResult) => ReactNode-The message. Defaults to the browser's validation message.
classNamestring-Additional classes to apply to the error.