codefast/ui

Command Palette

Search for a command to run...

Source
Form

Input Number

Numeric input with increment/decrement controls, min/max/step, and format options.

Examples

Stepper with bounds

Increment/decrement controls plus min, max, and step keep input valid.

Use the steppers or type — clamped to 0–99.

15 lines
import { InputNumber, InputNumberField, InputNumberStepper } from "@codefast/ui/input-number";
import { Label } from "@codefast/ui/label";

export function InputNumberQuantity() {
  return (
    <div className="grid w-full max-w-xs gap-1.5">
      <Label htmlFor="qty">Quantity</Label>
      <InputNumber id="qty" defaultValue={1} min={0} max={99}>
        <InputNumberField />
        <InputNumberStepper />
      </InputNumber>
      <p className="text-xs text-ui-muted">Use the steppers or type — clamped to 0–99.</p>
    </div>
  );
}

Formats & layouts

Format as currency with Intl options, or flank the field for a split layout.

36 lines
import {
  InputNumber,
  InputNumberDecrement,
  InputNumberField,
  InputNumberIncrement,
  InputNumberStepper,
} from "@codefast/ui/input-number";
import { Label } from "@codefast/ui/label";

export function InputNumberFormats() {
  return (
    <div className="grid w-full max-w-xs gap-4">
      <div className="grid gap-1.5">
        <Label htmlFor="price">Price</Label>
        <InputNumber
          id="price"
          defaultValue={9.99}
          min={0}
          step={0.01}
          formatOptions={{ style: "currency", currency: "USD" }}
        >
          <InputNumberField />
          <InputNumberStepper />
        </InputNumber>
      </div>
      <div className="grid gap-1.5">
        <Label htmlFor="cart">Cart quantity (split)</Label>
        <InputNumber id="cart" defaultValue={2} min={0} max={10}>
          <InputNumberDecrement />
          <InputNumberField className="text-center" />
          <InputNumberIncrement />
        </InputNumber>
      </div>
    </div>
  );
}

Disabled & invalid

Native disabled plus aria-invalid styling.

Must be 100 or less.

24 lines
import { InputNumber, InputNumberField, InputNumberStepper } from "@codefast/ui/input-number";
import { Label } from "@codefast/ui/label";

export function InputNumberStates() {
  return (
    <div className="grid w-full max-w-xs gap-4">
      <div className="grid gap-1.5">
        <Label htmlFor="in-disabled">Disabled</Label>
        <InputNumber id="in-disabled" defaultValue={5} disabled>
          <InputNumberField />
          <InputNumberStepper />
        </InputNumber>
      </div>
      <div className="grid gap-1.5">
        <Label htmlFor="in-invalid">Invalid</Label>
        <InputNumber id="in-invalid" defaultValue={150} min={0} max={100}>
          <InputNumberField aria-invalid />
          <InputNumberStepper />
        </InputNumber>
        <p className="text-xs text-rose-500">Must be 100 or less.</p>
      </div>
    </div>
  );
}

Usage

The minimal import and composition — see Examples below for styled, real-world variants.

10 lines
import { InputNumber, InputNumberField, InputNumberStepper } from "@codefast/ui/input-number";

export function InputNumberUsage() {
  return (
    <InputNumber defaultValue={1}>
      <InputNumberField />
      <InputNumberStepper />
    </InputNumber>
  );
}

Anatomy

How the parts nest — every slot the component exposes, in composition order.

InputNumber
├── InputNumberField
├── InputNumberStepper
├── InputNumberDecrement
└── InputNumberIncrement

Features

  • Composable parts — pair InputNumberField with InputNumberStepper for the stacked layout, or flank it with InputNumberDecrement and InputNumberIncrement for a split layout. Layout follows child order.
  • formatOptions (Intl.NumberFormatOptions) formats the displayed value, e.g. currency or percent, while the underlying value stays a plain number.
  • min/max/step clamp both typed and stepper-driven changes to a valid range, and only the button facing a bound is disabled.
  • Built-in loading/spinner support, e.g. while a computed value is being fetched.

API reference

Props for each part of the component. All native element props are also forwarded.

InputNumber

The container that owns the numeric state, bounds, and formatting. Compose the parts below as children.

valuenumber

The controlled numeric value. Or use defaultValue uncontrolled.

onChange(value?: number) => void

Called with the parsed number, or undefined when the field is cleared.

minnumber

The lower bound. Values are clamped to the range.

maxnumber

The upper bound. Values are clamped to the range.

stepnumber

The increment applied by the steppers.

Default1

formatOptionsIntl.NumberFormatOptions

Format the display, e.g. { style: "currency", currency: "USD" }.

InputNumberField

The editable spinbutton input.

aria-invalidboolean

Marks the field invalid and drives the container's invalid styling.

InputNumberStepper

A stacked chevron column with both increment and decrement controls — the default layout. Place it after the field.

classNamestring

Compose the column's styling.

InputNumberDecrement

A standalone decrement button for the split layout. Place it before the field.

childrenReactNode

Override the default minus icon.

InputNumberIncrement

A standalone increment button for the split layout. Place it after the field.

childrenReactNode

Override the default plus icon.

Accessibility

Built to be keyboard-navigable and screen-reader friendly out of the box.

KeyFunction
Arrow+UpIncrements by one step.
Arrow+DownDecrements by one step.
  • Renders a spinbutton with aria-valuenow / valuemin / valuemax.
  • Pair with a Label so the field has an accessible name.
  • Formatting is display-only — the underlying value stays numeric.

Guidelines

Conventions that keep usage consistent across an app.

Do

  • Set min, max, and step to express the valid range.
  • Use formatOptions for currency, percent, and units.

Don’t

  • Don’t use it for codes or phone numbers — use Input.
  • Don’t omit a label.

Explore further

Ready to integrate?

Follow the Getting Started guide to install @codefast/ui, or browse the full component gallery.