Number input
Takes a number that moves in steps (guests, quantity, a percentage), typed or nudged with the buttons at the end. For a number with no steps, such as a phone number, use a Text input.
Live preview
Rendered by the component itself from the same generated tokens as the rest of the site.
const [value, setValue] = useState<number | null>(4)
<NumberInput
label="Guests"
min={0}
max={10}
size="lg"
value={value}
onChange={setValue}
/>Installation
Install it with the shadcn CLI, along with everything it uses. New project? Set it up first, as Quick start shows.
npx shadcn@latest add https://www.graphite-ui.com/r/number-input.jsonimport { NumberInput } from '@/components/ui/number-input'Anatomy
Text input’s label, field and helper, the value 16 in, and the stepper at the end: decrement then increment, each a ghost icon-only Button square at the field’s height, each after a 1 × 20 divider.
- FieldRequiredThe governed Text input as a native number field, its browser spinner hidden. Every size, layout (fixed, inline, fluid), label, supporting text and state is Text input's.
- StepperRequiredThe kit's two action items at the end of the field, decrement then increment. Each is a ghost icon-only Button, square at the field's height (32, 40 or 48; 40 in Fluid), with the kit's 16px minus-small and plus-small, after a 1 × 20 outline divider. They are named "Decrement" and "Increment" and disable at the bounds. In Error and Warning the status glyph comes before them.
Variants
Size sets the field and the stepper together: 48, 40 or 32. Fluid is a 64px box with the label inside and 40px stepper buttons on the value row.
States
Text input’s states. Error and warning put the status glyph before the stepper, as the kit draws them. At a bound, the button that would pass it disables. Disabled and Read-only disable both.
API reference
Generated from the contract, versioned with it, and checked by drift-check. This table cannot describe props the component does not have.
| Prop | Type | Default | Notes |
|---|---|---|---|
| value / onChange | — | — | Controlled. A number, or null when the field is empty. What is typed stays as typed until it is a number; checking it against the range is the caller's. |
| min / max / step | — | — | Passed to the native field, so the arrow keys step and clamp as the stepper does. step is 1 by default. |
| Text input's other props | — | — | size, layout, label, helpText, errorText, warningText, disabled, readOnly and the rest. readOnly and disabled both disable the stepper. |
Design tokens
Every swatch is live. Change the source color in the header and this table repaints, because it reads the same roles the component does.
outlineThe dividers before each stepper button (the kit's Medium binds outline).spacingThe stepper running to the field's right edge, past the shell's 16 inset.
Usage
The contract's prohibitions, written as the choices you will actually face.
Do
- Set min and max, and say the range in the helper text.
- Check what is typed on the page, and say what is wrong with errorText.
- Pick a step that matches how the number moves: 1 for guests, 0.5 for hours.
- Keep the value short; the field is for numbers, not sentences.
Don’t
- Use it for numbers that are really identifiers: phone, card or postal numbers.
- Step past a bound. The buttons stop there.
- Hide the stepper. It is the reason to use this over a Text input.
- Restyle the field or the buttons. They are the governed Text input and Button.
Accessibility
What the component does for you, and what it leaves to you.
- Roles
- A native number field (
spinbutton), labelled, with its range and step, and two buttons after it. - Keyboard
- Arrow Up and Down step and clamp, as the buttons do. Tab reaches the field, then each button.
- Buttons
- “Decrement” and “Increment”. Each disables at the bound it would pass, and both with a disabled or read-only field.
- Focus
- The field takes Text input’s ring; each button its own.
Figma parity
The kit’s Number input page has two public sets, Default and Fluid, built from a private base and action item. The code is one component over Text input and Button.
| Kit axis | Values | Code | How it maps |
|---|---|---|---|
| Set | Default · Fluid | layout | fixed and fluid. |
| Size | Large · Medium · Small | size | 48, 40 and 32, the field and the stepper together. |
| State | Enabled · Focus · Error · Warning · Disabled · Read-only | — | Text input’s, with errorText, warningText, disabled and readOnly. Fluid’s Hover is the shell’s. |
| State | Skeleton | — | No counterpart by rule. |
| Action item | Enabled · Hover · Active · Focus · Disabled | — | The ghost Button’s own states. |
| Divider | border-subtle-01 · outline | — | The Large divider binds a variable that resolves to nothing; the code uses Medium’s outline at every size. |
| AI slug · Revert | Action items | — | Not built: the AI sets are ungoverned. |
