Number Field
A bounded numeric input with decrement and increment stepper buttons.
Show codeHide code
Installation
import { NumberField } from "@makeplane/propel/components/number-field";Usage
import { Icon } from "@makeplane/propel/components/icon";
import { IconButton } from "@makeplane/propel/components/icon-button";
import { NumberField, numberFieldStepperSize } from "@makeplane/propel/components/number-field";
import { Minus, Plus } from "lucide-react";
import * as React from "react";
export default function BasicDemo() {
const [value, setValue] = React.useState<number | null>(2);
return (
<NumberField
aria-label="Number of instances"
size="lg"
min={1}
max={64}
value={value}
onValueChange={setValue}
decrement={
<IconButton
variant="ghost"
size={numberFieldStepperSize.lg}
aria-label="Decrease"
icon={<Icon icon={Minus} />}
/>
}
increment={
<IconButton
variant="ghost"
size={numberFieldStepperSize.lg}
aria-label="Increase"
icon={<Icon icon={Plus} />}
/>
}
/>
);
}Examples
Sizes
The size axis sets the frame height (sm 24 / md 28 / lg 32). Pair each stepper IconButton
with numberFieldStepperSize[size] — button size is one rung below the field — so the ghost
controls sit flush inside the outer inset (1px border + 1px pad). No xs (20px) frame: grouped
controls omit halo rungs.
Show codeHide code
Disabled
Show codeHide code
Invalid
NumberField has no baked-in error prop. Wrap it in Field invalid (and optional FieldError)
so Base UI propagates data-invalid / aria-invalid to the input; the group border turns danger
via :has([data-invalid]).
Show codeHide code
Formatted
Pass format (an Intl.NumberFormatOptions) to format the displayed value — here as a currency amount stepped by 50.
Show codeHide code
Scrub
NumberFieldScrubArea is not baked into the ready-made. Assemble Base UI NumberField.Root with
propel’s scrub + group parts (see Storybook Components/NumberField → Scrub). Drag the scrub
label horizontally to change the value:
import { NumberField as BaseNumberField } from "@base-ui/react/number-field";
import {
NumberFieldGroup,
NumberFieldInput,
NumberFieldScrubArea,
NumberFieldScrubAreaCursor,
numberFieldStepperSize,
} from "@makeplane/propel/components/number-field";
import { NumberField as NumberFieldFrame } from "@makeplane/propel/elements/number-field";
<BaseNumberField.Root defaultValue={24} render={<NumberFieldFrame />}>
<NumberFieldScrubArea>
<span className="cursor-ew-resize">Amount</span>
<NumberFieldScrubAreaCursor />
</NumberFieldScrubArea>
<BaseNumberField.Group render={<NumberFieldGroup size="lg" />}>
{/* Decrement / Input / Increment grafts — same as the ready-made */}
</BaseNumberField.Group>
</BaseNumberField.Root>;API Reference
NumberField
The ready-made number field: a numeric input flanked by decrement / increment buttons. Drive it with `value`/`defaultValue` + `onValueChange`, bound it with `min`/`max`/`step`, and pass `format` (an `Intl.NumberFormatOptions`) to format the displayed value. Name the input with `aria-label` or `aria-labelledby` — the ready-made has no baked visible label. Grafts Base UI `NumberField` behavior onto the `elements/number-field` styled parts (`NumberField` root + `NumberFieldGroup` + `NumberFieldInput`) via `render`. The `decrement`/`increment` steppers are consumer-provided controls, grafted onto Base UI's `Decrement`/`Increment` behavior.
NumberFieldScrubArea
Drag-to-change scrubbing over the field's label region — Base UI behavior, no chrome.
NumberFieldScrubAreaCursor
The virtual cursor shown while scrubbing; pass your cursor svg as children.
NumberField
The styled number field frame — stacks the label, group, and messages. Base-UI-agnostic — graft the number field behavior in `components` via `<BaseNumberField.Root render={<NumberField/>} />`.
NumberFieldGroup
The styled bordered group wrapping the decrement / input / increment controls. Base-UI-agnostic — graft in `components` via `<BaseNumberField.Group render={<NumberFieldGroup/>} />`.
NumberFieldInput
The styled numeric input. Base-UI-agnostic — graft the number field behavior in `components` via `<BaseNumberField.Input render={<NumberFieldInput/>} />`.