Skip to main content

Installation

npm install @kuzenbo/core @kuzenbo/theme

Usage

import { NumberField } from "@kuzenbo/core";
import { Label } from "@kuzenbo/core";
import { useId } from "react";

function App() {
  const id = useId();

  return (
    <NumberField defaultValue={0} id={id}>
      <Label htmlFor={id}>Quantity</Label>
      <NumberField.Group>
        <NumberField.Decrement />
        <NumberField.Input />
        <NumberField.Increment />
      </NumberField.Group>
    </NumberField>
  );
}

Examples

Controlled NumberField

Manage value with React state.
import { NumberField } from "@kuzenbo/core";
import { Label } from "@kuzenbo/core";
import { useState, useId } from "react";

function ControlledExample() {
  const id = useId();
  const [quantity, setQuantity] = useState<number | null>(2);

  return (
    <NumberField
      id={id}
      max={10}
      min={1}
      onValueChange={setQuantity}
      step={1}
      value={quantity}
    >
      <Label htmlFor={id}>Cart quantity</Label>
      <NumberField.Group>
        <NumberField.Decrement />
        <NumberField.Input />
        <NumberField.Increment />
      </NumberField.Group>
    </NumberField>
  );
}

With Scrub Area

Drag the label to adjust the value.
import { NumberField } from "@kuzenbo/core";
import { Label } from "@kuzenbo/core";
import { useId } from "react";

function ScrubAreaExample() {
  const id = useId();

  return (
    <NumberField defaultValue={100} id={id}>
      <NumberField.ScrubArea className="flex items-center gap-2">
        <Label className="cursor-ew-resize" htmlFor={id}>
          Amount
        </Label>
        <NumberField.ScrubAreaCursor />
      </NumberField.ScrubArea>

      <NumberField.Group>
        <NumberField.Decrement />
        <NumberField.Input />
        <NumberField.Increment />
      </NumberField.Group>
    </NumberField>
  );
}

Currency Format

Format values as currency.
import { NumberField } from "@kuzenbo/core";
import { Label } from "@kuzenbo/core";
import { useId } from "react";

function CurrencyExample() {
  const id = useId();

  return (
    <NumberField
      defaultValue={2500}
      format={{ currency: "USD", style: "currency" }}
      id={id}
      largeStep={500}
      min={0}
      smallStep={10}
      step={50}
    >
      <Label htmlFor={id}>Monthly ad budget</Label>
      <NumberField.Group>
        <NumberField.Decrement />
        <NumberField.Input />
        <NumberField.Increment />
      </NumberField.Group>
    </NumberField>
  );
}

Pack Size Steps

Increment by custom step values.
import { NumberField } from "@kuzenbo/core";
import { Label } from "@kuzenbo/core";
import { useId } from "react";

function PackSizeExample() {
  const id = useId();

  return (
    <NumberField
      defaultValue={12}
      id={id}
      max={120}
      min={6}
      step={6}
    >
      <Label htmlFor={id}>Units per shipment</Label>
      <NumberField.Group>
        <NumberField.Decrement />
        <NumberField.Input />
        <NumberField.Increment />
      </NumberField.Group>
    </NumberField>
  );
}

Read Only

Display values without allowing edits.
import { NumberField } from "@kuzenbo/core";
import { Label } from "@kuzenbo/core";
import { useId } from "react";

function ReadOnlyExample() {
  const id = useId();

  return (
    <NumberField defaultValue={3} id={id} readOnly>
      <Label htmlFor={id}>Approved quantity</Label>
      <NumberField.Group>
        <NumberField.Decrement />
        <NumberField.Input />
        <NumberField.Increment />
      </NumberField.Group>
    </NumberField>
  );
}

Sizes

NumberFields support size variants inherited from the size context.
import { NumberField } from "@kuzenbo/core";
import { Label } from "@kuzenbo/core";
import { useId } from "react";

function SizesExample() {
  const id = useId();

  return (
    <div className="grid gap-3">
      <NumberField defaultValue={0} id={id} size="xs">
        <Label htmlFor={id}>XS quantity</Label>
        <NumberField.Group>
          <NumberField.Decrement />
          <NumberField.Input />
          <NumberField.Increment />
        </NumberField.Group>
      </NumberField>

      <NumberField defaultValue={0} id={id} size="sm">
        <Label htmlFor={id}>SM quantity</Label>
        <NumberField.Group>
          <NumberField.Decrement />
          <NumberField.Input />
          <NumberField.Increment />
        </NumberField.Group>
      </NumberField>

      <NumberField defaultValue={0} id={id} size="md">
        <Label htmlFor={id}>MD quantity</Label>
        <NumberField.Group>
          <NumberField.Decrement />
          <NumberField.Input />
          <NumberField.Increment />
        </NumberField.Group>
      </NumberField>
    </div>
  );
}

Props

NumberField

NumberField extends Base UI NumberField.Root props.
value
number | null
Controlled value of the number field.
defaultValue
number
Default value for uncontrolled usage.
onValueChange
(value: number | null) => void
Callback fired when the value changes.
min
number
Minimum allowed value.
max
number
Maximum allowed value.
step
number
default:"1"
Step increment for increment/decrement.
largeStep
number
Larger step increment (Shift + arrow keys).
smallStep
number
Smaller step increment (Alt/Option + arrow keys).
format
Intl.NumberFormatOptions
Number formatting options (e.g., currency, percent).
size
string
default:"md"
Size variant applied to all child components.Options: "xs" | "sm" | "md" | "lg" | "xl"
disabled
boolean
default:"false"
When true, disables the entire number field.
readOnly
boolean
default:"false"
When true, makes the field read-only.
required
boolean
default:"false"
When true, marks the field as required.
id
string
ID for associating with a label.
className
string
Additional CSS classes.
children
ReactNode
required
NumberField subcomponents.

Subcomponents

NumberField.Group

Container for the input and increment/decrement buttons.
<NumberField.Group>
  <NumberField.Decrement />
  <NumberField.Input />
  <NumberField.Increment />
</NumberField.Group>

NumberField.Input

The text input for entering numeric values.

NumberField.Increment

Button to increase the value by the step amount.

NumberField.Decrement

Button to decrease the value by the step amount.

NumberField.ScrubArea

Area that enables mouse drag to adjust value.

NumberField.ScrubAreaCursor

Custom cursor displayed during scrubbing.

TypeScript

import type {
  NumberFieldProps,
  NumberFieldInputProps,
  NumberFieldGroupProps,
  NumberFieldIncrementProps,
  NumberFieldDecrementProps,
  NumberFieldScrubAreaProps,
} from "@kuzenbo/core";

const CustomNumberField = (props: NumberFieldProps) => {
  return <NumberField {...props} />;
};

Accessibility

  • NumberField uses semantic ARIA spinbutton role
  • Supports keyboard navigation with arrow keys, Page Up/Down, Home, and End
  • Increment and decrement buttons are properly labeled
  • Screen readers announce value changes
  • Respects min/max bounds
  • Disabled and read-only states properly communicated

Build docs developers (and LLMs) love