shadcn.io is not affiliated with official shadcn/ui
Components
Free
Number Input for React Aria
Step buttons beside a field for a quantity, a price, or any other number. For React with React Aria, when a plain text field would accept junk.
Framework
UI library
A numeric field with buttons that step the value up and down. Typing still works.
Use it when the value has a minimum, a maximum, or a fixed step. A free-form amount with only a symbol beside it is an Input. In React, React Aria keeps the value in range and steps it with the buttons.
Installation
Sign in to get your install command
Free with an account. It carries your token, so the CLI installs everything in one go.
Increments the value of the number input by a predefined step.
Arrow Down
Decrements the value of the number input by a predefined step.
Shift+Arrow Up
Increments the value of the number input by the largeStep amount.
Shift+Arrow Down
Decrements the value of the number input by the largeStep amount.
Alt+Arrow Up
Increments the value of the number input by the smallStep amount.
Alt+Arrow Down
Decrements the value of the number input by the smallStep amount.
Home
Sets the value of the number input to its minimum allowed value.
End
Sets the value of the number input to its maximum allowed value.
Enter
Submits the value entered in the number input.
Space
Activate the focused control.
Demos
Controlled
The number lives in your state, starting at 1, and the line under the field only settles once it reads 3. Hold value when another part of the screen depends on it, or when you want to refuse a change before it sticks. The callback hands you a string, so a cleared field stays empty instead of becoming zero. Leave it uncontrolled if nothing else needs to read the number.
Disabled
disabled locks the field and both step buttons. Use it for a quantity the current plan or permission cannot change, while still showing the number that would have been edited. It is not a read-only caption: the control stays in the form, it just will not take input. Pair it with nearby text that says why it is off, or people will keep hitting the buttons.
Field Only
The group holds the input and nothing else. Drop the increment and decrement buttons when the row is narrow, like a table cell or a toolbar, and people will type the number. You still get a numeric field rather than a text box that only looks like one. If the step is awkward to type, put the buttons back.
Formatted
formatOptions takes Intl.NumberFormat options. Here the style is currency and the currency is USD, so 19 shows as dollars while the stored value stays a number. Use it for money or percents when the field should speak the format, not when you only want a symbol painted beside the box. A prefix you design yourself belongs in an input group instead.
Invalid
invalid is the error paint, before or after submit. Turn it on when the number fails a rule the field itself does not know, such as a stock check, and put the reason in a description next to it. The prop does not block typing or stepping. It only marks the control, so the message has to say what is wrong.
Mouse Wheel
allowMouseWheel lets a scroll over the focused field step the value. It stays off unless you set it, because a page scroll that crosses the input should not change a quantity. Turn it on in a tool panel where the pointer is already on the control and a wheel nudge is faster than the buttons. Leave it off in a long form.
Range
min is 0 and max is 10, with 5 as the start. The buttons stop at the ends, and a typed value outside that span is pulled back in. Set both when a percentage, a rating, or a small count cannot leave a known band. A step still applies inside the band, and if you only need to block negatives, a minimum is enough.
Scrub
NumberInputScrubber wraps the Quantity label, and dragging across it moves the value, the gesture design tools use when tiny plus and minus buttons are the slow path. Keep the buttons for people who click or use a keyboard. The scrubber is an extra handle, not a replacement for the field. Skip it where a drag fights page scrolling.
Size Lg
size set to lg raises the field and the step buttons. Pick the large size when this number is the main control on the screen, or when a coarse pointer makes the default hit area feel mean. It is the same component, not a different widget. Match it to other large fields in the form so the row does not jump.
Size Md
md is the default size, shown on its own so you can compare it with the small and large fields. Leave size unset in most forms. It lines up with the usual input height. Reach for another size only when the surrounding density actually changes, not to make one field look more important than its label.
Size Sm
size set to sm tightens the field for a toolbar, a table, or a side panel with too many rows. The buttons shrink with it, so do not use the small size as the only control on a phone. If the number is rare and the chrome should get out of the way, this is the one. Keep a visible label, because the shorter box is easy to miss.
Step
Two fields, one with step of 5 and one with step of 0.1. The first jumps in whole units, which fits inventory, and the second moves in tenths, which fits a measurement. The buttons move by that step, so pick one people can predict. A step of 7 on a price looks broken even when the math is right.
With Field
The number sits in a Field with the label Quantity and a description that says 0 to 100. min and max match that sentence. Use Field when the number needs a label, a hint, and later an error, instead of a placeholder that disappears as soon as someone types. The description is the place for the range, so do not hide it in an accessible name only.
With Input Group
The number sits in an Input Group with a euro sign before it and EUR after it, and there are no step buttons. The field has its own accessible name, because the symbols are not a label. Use this when the unit is chrome and formatOptions would force a locale currency format you do not want. The group is layout, and the number input holds the value.
FAQ
How do I control the Number Input value in React?
Pass value and onValueChange on NumberInput. The callback gets { value } as a string, so an empty field stays empty instead of turning into 0; convert it with Number(value) when you save it.
See the Controlled demo above.
How do I set a minimum, maximum and step on the Number Input?
Set min, max and step on NumberInput. The increment and decrement buttons stop at the bounds and move by step, and a value typed outside the range is clamped back into it. The Range and Step demos show both.
How do I format the Number Input as currency or a percentage?
Pass formatOptions, which takes Intl.NumberFormat options: { style: "currency", currency: "USD" } shows dollars and { style: "percent" } a percentage. The Formatted demo above shows one.
Can the Number Input change with the mouse wheel?
Yes: set allowMouseWheel on NumberInput, and scrolling over the focused field steps the value. It's off by default so that scrolling the page past a field never changes a value by accident.
How do I use the React Aria Number Input in a form?
Give NumberInput a name, and its value submits with the form.
Wrap it in Field for a label, a description and an error message, as in the With Field demo.
React Hook Form, TanStack Form and Formisch each have a number field demo in the forms guides.
How do I let people drag to change the Number Input value?
Add NumberInputScrubber, usually around the label: dragging across it scrubs the value up and down by step. The Scrub demo above puts one on the label.
Is the React Aria Number Input accessible?
Yes. ArrowUp and ArrowDown change the value by step, and the increment and decrement buttons carry their own labels.
Pair it with a visible label, as in the With Field demo.
How do I install the Number Input in React?
Run the command from the Installation section above: it adds the Number Input and the parts it uses to your project's ui folder.
The shadcn CLI writes components/ui/number-input.tsx, a file you own and can edit.
API Reference
NumberInput
PropType
value?number | string
The value. Pair with onValueChange to control it.
Type
number | string
defaultValue?number | string
The value it starts with, when you don't control value.
Type
number | string
onValueChange?(details: { value: string }) → void
Called when the value changes.
Type
(details: { value: string }) → void
Parameters
details – { value: string }
Returns
void
allowMouseWheel?boolean
Whether to allow mouse wheel to change the value.
Default
false
disabled?boolean
Whether it's disabled.
formatOptions?Intl.NumberFormatOptions
The options to pass to the Intl.NumberFormat constructor.
id?string
The element's id.
invalid?boolean
Whether it's invalid.
Default
false
max?number
The highest value.
Default
Number.MAX_SAFE_INTEGER
min?number
The lowest value.
Default
Number.MIN_SAFE_INTEGER
name?string
The name it submits with its form.
readOnly?boolean
Whether the number input is readonly.
size?"sm" | "md" | "lg"
The size.
Type
"sm" | "md" | "lg"
Default
"md"
step?number
How much each step changes the value.
Default
1
className?string
Merged with the part's classes.
AttributeType
data-disabled?string
Present when disabled is true.
data-focus?string
Present when focused is true.
data-invalid?string
Present when invalid is true.
data-scrubbing?string
Present when scrubbing is true.
NumberInputGroup
data-slot="number-field-group"
PropType
children?ChildrenOrFunction<T>
The children of the component. A function may be provided to alter the children based on
component state.
Type
ChildrenOrFunction<T>
isDisabled?boolean
Whether the group is disabled.
isInvalid?boolean
Whether the group is invalid.
isReadOnly?boolean
Whether the group is read only.
role?"group" | "region" | "presentation"
An accessibility role for the group. By default, this is set to group.
Use region when the contents of the group is important enough to be
included in the page table of contents. Use presentation if the group
is visual only and does not represent a semantic grouping of controls.
Type
"group" | "region" | "presentation"
Default
"group"
slot?string | null
A slot name for the component. Slots allow the component to receive props from a parent
component. An explicit null value indicates that the local props completely override all
props received from a parent.
Type
string | null
style?StyleOrFunction<T>
The inline [style](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/style) for the
element. A function may be provided to compute the style based on component state.
Type
StyleOrFunction<T>
render?DOMRenderFunction<E, T>
Overrides the default DOM element with a custom render function.
This allows rendering existing components with built-in styles and behaviors
such as router links, animation libraries, and pre-styled components.
Requirements:
- You must render the expected element type (e.g. if <button> is expected, you cannot render an
<a>).
- Only a single root DOM element can be rendered (no fragments).
- You must pass through props and ref to the underlying DOM element, merging with your own prop
as appropriate.
Type
DOMRenderFunction<E, T>
className?string
Merged with the part's classes.
AttributeType
data-disabled?string
Present when disabled is true.
data-focus?string
Present when focused is true.
data-invalid?string
Present when invalid is true.
data-scrubbing?string
Present when scrubbing is true.
NumberInputDecrement
data-slot="number-field-decrement"
PropType
children?ReactNode
The content to display in the button.
clickEffect?boolean
Apply a click effect to the button.
Default
true
download?LinkPrimitiveProps["download"]
With href, downloads the linked file instead of opening it.
href?string
When set, the button renders as a link.
isDisabled?boolean
Whether the button is disabled.
isLoading?boolean
Show a loading indicator.
Default
false
pill?boolean
Fully rounded corners.
Default
false
rel?string
With href, the link's relationship to the page (noreferrer, …).
An enumerated attribute that defines what action label or icon to preset for the enter key on
virtual keyboards. See
[MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/enterkeyhint).