Number Field

PreviousNext

A number input field that allows users to enter and adjust numeric values with optional formatting.

import { NumberField } from "@/components/ui/number-field"

export function NumberFieldDemo() {
  return (
    <div className="grid w-full max-w-md gap-4">
      <NumberField
        label="Quantity"
        defaultValue={5}
        minValue={0}
        maxValue={100}
      />
    </div>
  )
}

Installation

pnpm dlx shadcn@latest add number-field

Usage

import { NumberField } from "@/components/ui/number-field"
<NumberField
  label="Quantity"
  placeholder="Enter quantity"
  defaultValue={5}
  minValue={0}
  maxValue={100}
/>

Examples

Default

A basic number field with a label and default value.

import { NumberField } from "@/components/ui/number-field"

export function NumberFieldDemo() {
  return (
    <div className="grid w-full max-w-md gap-4">
      <NumberField
        label="Quantity"
        defaultValue={5}
        minValue={0}
        maxValue={100}
      />
    </div>
  )
}

With Description

Add helpful context with a description below the label.

Enter the product price in dollars.
import { NumberField } from "@/components/ui/number-field"

export function NumberFieldWithDescription() {
  return (
    <div className="grid w-full max-w-md gap-4">
      <NumberField
        label="Price"
        description="Enter the product price in dollars."
        defaultValue={29.99}
        minValue={0}
        formatOptions={{
          style: "currency",
          currency: "USD",
        }}
      />
    </div>
  )
}
<NumberField
  label="Price"
  description="Enter the product price in dollars."
  placeholder="0.00"
  defaultValue={29.99}
  minValue={0}
  formatOptions={{
    style: "currency",
    currency: "USD",
  }}
/>

Disabled

Disable the number field to prevent user interaction.

import { NumberField } from "@/components/ui/number-field"

export function NumberFieldDisabled() {
  return (
    <div className="grid w-full max-w-md gap-4">
      <NumberField label="Items in stock" defaultValue={42} isDisabled />
    </div>
  )
}
<NumberField
  label="Items in stock"
  defaultValue={42}
  isDisabled
/>

With Validation

Use validation to ensure values meet your requirements.

Enter your age (must be between 18 and 100)
"use client"

import { useState } from "react"

import { NumberField } from "@/components/ui/number-field"

export function NumberFieldValidation() {
  const [value, setValue] = useState(150)

  return (
    <div className="grid w-full max-w-md gap-4">
      <NumberField
        label="Age"
        description="Enter your age (must be between 18 and 100)"
        value={value}
        onChange={setValue}
        minValue={18}
        maxValue={100}
        isRequired
        errorMessage={(validation) => {
          if (validation.validationErrors.includes("rangeUnderflow")) {
            return "You must be at least 18 years old"
          }
          if (validation.validationErrors.includes("rangeOverflow")) {
            return "Age must be 100 or less"
          }
          if (validation.validationErrors.includes("valueMissing")) {
            return "Age is required"
          }
          return "Invalid age"
        }}
      />
    </div>
  )
}
"use client"
 
import { useState } from "react"
import { NumberField } from "@/components/ui/number-field"
 
export default function Example() {
  const [value, setValue] = useState(150)
 
  return (
    <NumberField
      label="Age"
      description="Enter your age (must be between 18 and 100)"
      value={value}
      onChange={setValue}
      minValue={18}
      maxValue={100}
      isRequired
      errorMessage={(validation) => {
        if (validation.validationErrors.includes("rangeUnderflow")) {
          return "You must be at least 18 years old"
        }
        if (validation.validationErrors.includes("rangeOverflow")) {
          return "Age must be 100 or less"
        }
        if (validation.validationErrors.includes("valueMissing")) {
          return "Age is required"
        }
        return "Invalid age"
      }}
    />
  )
}

Percentage Format

Format numbers as percentages with custom fraction digits.

Enter the discount percentage
import { NumberField } from "@/components/ui/number-field"

export function NumberFieldPercentage() {
  return (
    <div className="grid w-full max-w-md gap-4">
      <NumberField
        label="Discount"
        description="Enter the discount percentage"
        defaultValue={15}
        minValue={0}
        maxValue={100}
        formatOptions={{
          style: "percent",
          minimumFractionDigits: 0,
          maximumFractionDigits: 2,
        }}
      />
    </div>
  )
}
<NumberField
  label="Discount"
  description="Enter the discount percentage"
  defaultValue={15}
  minValue={0}
  maxValue={100}
  formatOptions={{
    style: "percent",
    minimumFractionDigits: 0,
    maximumFractionDigits: 2,
  }}
/>

Custom Steppers with Button Group

Use the ButtonGroup component to create custom steppers.

Use the buttons to adjust the quantity
"use client"

import { useState } from "react"
import { MinusIcon, PlusIcon } from "lucide-react"
import { NumberField as AriaNumberField } from "react-aria-components"

import { Button } from "@/components/ui/button"
import { ButtonGroup } from "@/components/ui/button-group"
import {
  FieldDescription,
  FieldLabel,
  fieldVariants,
} from "@/components/ui/field"

import { Input } from "../ui/input"

export function NumberFieldCustomSteppers() {
  const [value, setValue] = useState(10)

  return (
    <div className="grid w-full max-w-md gap-4">
      <AriaNumberField
        value={value}
        onChange={setValue}
        minValue={0}
        maxValue={100}
        className={fieldVariants()}
      >
        <FieldLabel>Quantity</FieldLabel>
        <ButtonGroup>
          <Button slot="decrement" aria-label="Decrease" variant="outline">
            <MinusIcon className="h-4 w-4" />
          </Button>
          <Input />
          <Button slot="increment" aria-label="Increase" variant="outline">
            <PlusIcon className="h-4 w-4" />
          </Button>
        </ButtonGroup>
        <FieldDescription>
          Use the buttons to adjust the quantity
        </FieldDescription>
      </AriaNumberField>
    </div>
  )
}

API Reference

NumberField

The NumberField component extends the React Aria NumberField component with consistent styling and built-in label, description, and error message support.

PropTypeDefaultDescription
labelstring-Label text for the number field
descriptionstring-Helper text displayed below the label
errorMessagestring | ((validation: ValidationResult) => string)-Error message for validation failures
defaultValuenumber-Default value (uncontrolled)
valuenumber-Current value (controlled)
onChange(value: number) => void-Callback when value changes
minValuenumber-Minimum allowed value
maxValuenumber-Maximum allowed value
stepnumber1Amount to increment/decrement
isDisabledbooleanfalseWhether the number field is disabled
isRequiredbooleanfalseWhether the number field is required
isReadOnlybooleanfalseWhether the number field is read-only
formatOptionsIntl.NumberFormatOptions-Options for number formatting (currency, percent, etc.)

Apart from the props above, the NumberField component also supports all props from the react-aria-components NumberField component. See the React Aria documentation for more details.

Features

  • Accessible - Built on React Aria Components with full keyboard navigation and screen reader support
  • Validation - Built-in validation with custom error messages
  • Formatting - Support for currency, percentage, and custom number formats using Intl.NumberFormat
  • Range Constraints - Enforce minimum and maximum values
  • Step Increment - Customize increment/decrement step values
  • Localization - Automatic locale-aware number formatting
  • Input - For text input
  • Field - For custom form field composition
  • Input Group - For adding additional context to inputs