Getting Started
Components
- Accordion
- Alert
- Alert Dialog
- Avatar
- Badge
- Button
- Calendar
- Card
- Carousel
- Chart
- Checkbox
- Circular Progress
- Collapsible
- Compare Slider
- Dialog
- Dropdown Menu
- Gauge
- Input
- Label
- Marquee
- Navigation Menu
- Number Field
- Pagination
- Popover
- Progress
- Radio Group
- Scroll Area
- Select
- Separator
- Sheet
- Sidebar
- Skeleton
- Slider
- Switch
- Table
- Tabs
- Textarea
- Toggle
- Tooltip
Demos
A number field with a scrub label and step buttons.
"use client"
import {
NumberField,
NumberFieldDecrement,
NumberFieldGroup,
NumberFieldIncrement,
NumberFieldInput,
NumberFieldScrubArea,
} from "@workspace/ui/components/number-field"
export function NumberFieldDemo() {
return (
<NumberField defaultValue={70} min={20} max={300} className="max-w-48">
<NumberFieldScrubArea label="Weight (kg)" />
<NumberFieldGroup>
<NumberFieldDecrement />
<NumberFieldInput />
<NumberFieldIncrement />
</NumberFieldGroup>
</NumberField>
)
}
Usage
Use a number field whenever someone types a number, like weight, a dose, or a count. Use it instead of an Input with type="number".
Import it from @workspace/ui/components/number-field.
Anatomy
- NumberField — The root. Holds the value, limits, and size.
- NumberFieldScrubArea — The label. Drag it left or right to change the value.
- NumberFieldGroup — The pill-shaped frame around the buttons and input.
- NumberFieldDecrement — The minus button.
- NumberFieldInput — The text input, centred.
- NumberFieldIncrement — The plus button.
Notes
- Arrow Up and Arrow Down step by
step. Hold Shift forlargeStepand Alt forsmallStep. - Values are clamped to
minandmaxwhen the field loses focus. - Without
NumberFieldScrubArea, giveNumberFieldInputanaria-label. NumberFieldScrubAreamust be inside aNumberField. It throws an error otherwise.
Examples
Sizes
sm, default, and lg. They match the heights of Input.
"use client"
import {
NumberField,
NumberFieldDecrement,
NumberFieldGroup,
NumberFieldIncrement,
NumberFieldInput,
} from "@workspace/ui/components/number-field"
const sizes = ["sm", "default", "lg"] as const
export function NumberFieldSizes() {
return (
<div className="flex w-full max-w-48 flex-col gap-4">
{sizes.map((size) => (
<NumberField key={size} size={size} defaultValue={5}>
<NumberFieldGroup>
<NumberFieldDecrement />
<NumberFieldInput aria-label={`Quantity, ${size}`} />
<NumberFieldIncrement />
</NumberFieldGroup>
</NumberField>
))}
</div>
)
}
Decimal format
Set step and format to step by tenths and always show one decimal place.
"use client"
import {
NumberField,
NumberFieldDecrement,
NumberFieldGroup,
NumberFieldIncrement,
NumberFieldInput,
NumberFieldScrubArea,
} from "@workspace/ui/components/number-field"
export function NumberFieldFormat() {
return (
<NumberField
defaultValue={5.6}
min={3}
max={15}
step={0.1}
format={{ minimumFractionDigits: 1, maximumFractionDigits: 1 }}
className="max-w-48"
>
<NumberFieldScrubArea label="HbA1c (%)" />
<NumberFieldGroup>
<NumberFieldDecrement />
<NumberFieldInput />
<NumberFieldIncrement />
</NumberFieldGroup>
</NumberField>
)
}
Controlled
Hold the value with value and onValueChange. The value is null when the input is empty.
Value: 8
"use client"
import * as React from "react"
import {
NumberField,
NumberFieldDecrement,
NumberFieldGroup,
NumberFieldIncrement,
NumberFieldInput,
NumberFieldScrubArea,
} from "@workspace/ui/components/number-field"
export function NumberFieldControlled() {
const [value, setValue] = React.useState<number | null>(8)
return (
<div className="flex w-full max-w-48 flex-col gap-3">
<NumberField value={value} onValueChange={setValue} min={0} max={24}>
<NumberFieldScrubArea label="Sleep (hours)" />
<NumberFieldGroup>
<NumberFieldDecrement />
<NumberFieldInput />
<NumberFieldIncrement />
</NumberFieldGroup>
</NumberField>
<p className="text-muted-foreground text-xs">
Value: {value ?? "empty"}
</p>
</div>
)
}
Disabled
A number field that cannot be changed.
"use client"
import {
NumberField,
NumberFieldDecrement,
NumberFieldGroup,
NumberFieldIncrement,
NumberFieldInput,
NumberFieldScrubArea,
} from "@workspace/ui/components/number-field"
export function NumberFieldDisabled() {
return (
<NumberField defaultValue={172} disabled className="max-w-48">
<NumberFieldScrubArea label="Height (cm)" />
<NumberFieldGroup>
<NumberFieldDecrement />
<NumberFieldInput />
<NumberFieldIncrement />
</NumberFieldGroup>
</NumberField>
)
}
Props
These are the props you set most often. Unlisted props pass through to the underlying element.
NumberField
| Prop | Type | Default | Description |
|---|---|---|---|
defaultValue | number | — | Starting value. |
value | number or null | — | Value when controlled. |
onValueChange | (value) => void | — | Called when the value changes. |
min | number | — | Lowest allowed value. |
max | number | — | Highest allowed value. |
step | number or "any" | 1 | Amount the buttons and arrow keys change the value by. |
smallStep | number | 0.1 | Step with Alt held. |
largeStep | number | 10 | Step with Shift held. |
format | Intl.NumberFormatOptions | — | How the value is shown, such as decimal places or units. |
size | "sm", "default", or "lg" | default | Height of the field. |
disabled | boolean | false | Blocks input and dims the field. |
name | string | — | Field name for form submission. |
NumberFieldScrubArea
| Prop | Type | Default | Description |
|---|---|---|---|
label | string | — | Required. The label text, linked to the input. |