# Number field

> A stepper you can press, hold, scrub, or type into. Push past a limit and it pushes back.

Component · Free · https://lodeui.com/components/number-field

## When to use

- Small integer quantities: seats, guests, items, replicas.
- Values people adjust by feel, where dragging to scrub is faster than typing.

## When not to use

- Large or precise numbers like prices or IDs: use an input.
- A continuous range: use a slider.
- Values with units that need typing (e.g. 12.5 GB): use an input with a trailing unit.

## Installation

```bash
npx shadcn@latest add https://lodeui.com/r/number-field.json
```

npm dependencies: motion, lucide-react

## Usage

```tsx title="example.tsx"
import { NumberField } from "@/components/ui/number-field";

export function Seats({ seats, setSeats }: { seats: number; setSeats: (n: number) => void }) {
  return (
    <NumberField
      aria-label="Seats"
      value={seats}
      onValueChange={setSeats}
      min={1}
      max={50}
      format={(n) => `${n} ${n === 1 ? "seat" : "seats"}`}
    />
  );
}
```

## API reference

### NumberField

A spinbutton with decrement and increment buttons, press-and-hold repeat, drag-to-scrub and limit feedback.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| value / defaultValue | `number` | `0` | Controlled or uncontrolled value. |
| onValueChange | `(value: number) => void` | – | Called on every change, including while scrubbing. |
| min / max | `number` | `-∞ / ∞` | Bounds. Pushing past them nudges the field instead of changing the value. |
| step | `number` | `1` | Increment for buttons, arrow keys and each 10px of scrubbing. |
| format | `(value: number) => string` | `String` | Display text, also used as aria-valuetext. |
| scrub | `boolean` | `true` | Drag the value horizontally to change it. |
| size | `"sm" \| "md"` | `"md"` | 224px or 304px wide, with smaller or larger digits. |
| disabled | `boolean` | – | Dims and blocks interaction. |

## Keyboard interactions

| Keys | Action |
| --- | --- |
| ↑ / → | Increase by step |
| ↓ / ← | Decrease by step |
| Page Up / Page Down | Change by ten steps |
| Home / End | Jump to min or max |

## Accessibility

- The value is a role="spinbutton" with aria-valuenow, min, max and valuetext.
- The − and + buttons are labelled and skipped in tab order, as the spinbutton pattern recommends.
- Keyboard activation of the buttons still works for assistive tech.

## Motion

- Digits roll up when the value grows and down when it shrinks (spring, 320ms).
- At a limit the field nudges 7px toward the push and springs back.
- Scrubbing past a limit rubber-bands with square-root resistance.

## Responsive behavior

- Fixed widths that shrink with max-w-full in narrow containers.
- Scrubbing uses pointer events, so it works with touch.

## Performance

- Hold-to-repeat speeds up from 380ms to 50ms intervals and stops on pointer up or leave.
- Only the value text re-renders on change.

## Notes for AI

- Use format for units so screen readers hear them too.
- For non-integers set step (e.g. 0.5) and format with toFixed.

## Related

- [Slider](https://lodeui.com/components/slider) — Drag, click or use the keys. The value rides above the thumb while you drag.
- [Input](https://lodeui.com/components/input) — Text, search, password and URL inputs with icons, clear button and a labelled Field wrapper.
- [Segmented control](https://lodeui.com/components/segmented-control) — A compact single choice with a pill that slides to the selection.
