- Alert
- Avatar
- Badge
- Breadcrumb
- Button
- Button Group
- Calendar
- Card
- Carousel
- Chart
- Checkbox
- Checkbox Group
- ComboBox
- Command
- Data Table
- Date Field
- Date Picker
- Date Range Picker
- Dialog
- Disclosure
- Disclosure Group
- Drawer
- Empty
- Field
- File Trigger
- Form
- Grid List
- Hover Card
- Input
- Input Group
- Input OTP
- Item
- Kbd
- Label
- ListBox
- Menu
- Number Field
- Pagination
- Popover
- Progress Bar
- Radio Group
- Range Calendar
- Resizable
- Search Field
- Select
- Separator
- Sheet
- Sidebar
- Skeleton
- Slider
- Sonner
- Spinner
- Switch
- Table
- Tabs
- Tag Group
- Text Field
- Textarea
- Time Field
- Toast
- Toggle Button
- Toggle Button Group
- Tooltip
- Tree
- Typography
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
pnpmnpmyarnbunpnpm 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.
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.
"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.
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 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.
| Prop | Type | Default | Description |
|---|---|---|---|
label | string | - | Label text for the number field |
description | string | - | Helper text displayed below the label |
errorMessage | string | ((validation: ValidationResult) => string) | - | Error message for validation failures |
defaultValue | number | - | Default value (uncontrolled) |
value | number | - | Current value (controlled) |
onChange | (value: number) => void | - | Callback when value changes |
minValue | number | - | Minimum allowed value |
maxValue | number | - | Maximum allowed value |
step | number | 1 | Amount to increment/decrement |
isDisabled | boolean | false | Whether the number field is disabled |
isRequired | boolean | false | Whether the number field is required |
isReadOnly | boolean | false | Whether the number field is read-only |
formatOptions | Intl.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
Related
- Input - For text input
- Field - For custom form field composition
- Input Group - For adding additional context to inputs