Chuyển tới nội dung
Component

NumberInput

A numeric field with stacked step buttons at its end, for quantities, counts and amounts with a known step (cart quantity, guest count, seats). It is a text input (type text, inputmode decimal), never type number, so it can show formatted text such as 1.000 ₫. Use Input for free text, Slider when the exact value matters less than the range.

Xem trước

Props

  • min
  • max
  • step
  • largeStep
  • clampOnBlur
  • size
  • disabled
  • readOnly
  • invalid
  • required
  • name
  • showStepButtons
  • decrementLabel
  • incrementLabel
  • locale
  • label
  • defaultValue
import { Field, FieldControl, FieldLabel } from "@/components/ui/field"
import { NumberInput } from "@/components/ui/number-input"

<Field className="w-full max-w-sm" invalid={false}>
  <FieldLabel>Quantity</FieldLabel>
  <FieldControl>
    <NumberInput min={0} max={10} step={1} defaultValue={3} />
  </FieldControl>
</Field>

Cách dùng

Khi nào dùng

  • Enter or adjust a quantity whose valid steps are known, such as seats, guests or items in a cart.
  • Show a number with locale-aware formatting, such as a currency amount, while keeping it editable.

Khi nào không dùng

  • Enter an identifier such as a phone or card number. Use Input with numeric input mode.
  • Pick a position where the range matters more than the exact number. Use Slider.
  • Enter free-form text. Use Input.

Biến thể

size

Control height from size-element-sm/md/lg/xl. Pick per region; md is the default.

  • sm

  • mdmặc định

  • lg

  • xl

import { Field, FieldControl, FieldLabel } from "@/components/ui/field"
import { NumberInput } from "@/components/ui/number-input"

<Field className="w-full max-w-sm" invalid={false}>
  <FieldLabel>Quantity</FieldLabel>
  <FieldControl>
    <NumberInput min={0} max={10} size="sm" step={1} defaultValue={3} />
  </FieldControl>
</Field>
<Field className="w-full max-w-sm" invalid={false}>
  <FieldLabel>Quantity</FieldLabel>
  <FieldControl>
    <NumberInput min={0} max={10} step={1} defaultValue={3} />
  </FieldControl>
</Field>
<Field className="w-full max-w-sm" invalid={false}>
  <FieldLabel>Quantity</FieldLabel>
  <FieldControl>
    <NumberInput min={0} max={10} size="lg" step={1} defaultValue={3} />
  </FieldControl>
</Field>
<Field className="w-full max-w-sm" invalid={false}>
  <FieldLabel>Quantity</FieldLabel>
  <FieldControl>
    <NumberInput min={0} max={10} size="xl" step={1} defaultValue={3} />
  </FieldControl>
</Field>

Ví dụ

Quantity

import { Field } from "@/components/ui/field"
import { NumberInput } from "@/components/ui/number-input"

<Field label="Quantity">
  <NumberInput defaultValue={1} min={1} max={10} />
</Field>

Controlled

const [guests, setGuests] = React.useState(2)
import { NumberInput } from "@/components/ui/number-input"

const [guests, setGuests] = React.useState(2)
<NumberInput value={guests} onValueChange={setGuests} min={1} max={16} size="lg" />

Currency

import { NumberInput } from "@/components/ui/number-input"

<NumberInput defaultValue={1000} step={1000} min={0} locale="vi-VN" formatOptions={{ style: "currency", currency: "VND" }} />

Nên và không nên

Nên

  • Set min and max whenever the domain has them (a quantity is at least 1).
  • Use inside Field so the label and error are linked.
  • Use lg or xl on touch-first screens so the buttons are easy to hit.

Không nên

  • Do not use for numbers that are identifiers (card number, phone, postal code); use Input with inputmode numeric.
  • Do not use for a range where position matters more than the value; use Slider.
  • Do not fire network requests on every step; debounce in the app.

Props

TênKiểuGiá trịMặc địnhMô tả
valuenumber | null--The value when you control it. null is an empty field.
defaultValuenumber | null-nullThe value on first render when you do not control it.
onValueChange(value: number | null) => void--Called with the committed number (after parse, clamp and step rounding) on a step key, a button press, Enter or blur, and with null when the field is cleared. Not called for text that does not parse (the field shows invalid instead) or on every keystroke.
minnumber--Lowest value. Buttons and ArrowDown stop here.
maxnumber--Highest value. Must be greater than min.
stepnumber-1Amount per step.
largeStepnumber--Amount for PageUp and PageDown. Defaults to step times 10.
clampOnBlurboolean-trueClamp and round a typed value when the field loses focus. When false, an out-of-range value stays and the field shows invalid.
sizeenumsm | md | lg | xlmdControl height from size-element-sm/md/lg/xl. Pick per region; md is the default.
disabledboolean-falseNo typing, no buttons, disabled look.
readOnlyboolean-falseThe value can be read, focused and copied but not changed; the step buttons are hidden and the field drops its fill. The inner input gets the native readOnly attribute (its bindings use :read-only); the root group sets data-read-only.
invalidboolean-falseSets aria-invalid and the danger border.
requiredboolean-falseNative required on the input.
namestring--Form field name; the submitted value is the plain number, not the formatted text.
showStepButtonsboolean-trueShow the decrement and increment buttons. Hide them for dense tables where arrow keys are enough.
decrementLabelstring-DecreaseAccessible name of the decrement button. Vietnamese: Giảm.
incrementLabelstring-IncreaseAccessible name of the increment button. Vietnamese: Tăng.
localestring--BCP 47 locale for formatting and for the separators used in parsing. Defaults to the document language.
formatOptionsIntl.NumberFormatOptions--How the value is shown when the field is not being edited and in aria-valuetext: currency, percent, unit, fraction digits. While focused the field shows the plain localized number.

Slot

TênBắt buộcMô tả
leadingTextkhôngOptional fixed text before the number, such as a currency (follows the planned Input leadingText).
trailingTextkhôngOptional unit after the number, such as kg.

Cấu tạo

PhầnMô tả
rootThe group that draws the field box: border, fill, corners, focus ring through :has(). The step buttons sit inside it at the end.
inputThe text input with role spinbutton, font-size-input, tabular numbers, aligned to the start (left in LTR, right in RTL), with the step buttons at the end.
decrementThe lower ghost icon button of the stack at the end (Tabler chevron down, 16px); first of the pair on touch.
incrementThe upper ghost icon button of the stack at the end (Tabler chevron up, 16px); second of the pair on touch.
placeholderThe placeholder text of the input, in secondary text like Input.

Khả năng truy cập

Role: spinbutton

PhímHành động
ArrowUp / ArrowDownIncrease or decrease by step, clamped to min and max.
PageUp / PageDownIncrease or decrease by largeStep.
Home / EndSet min or max when they are set.
EnterCommit the typed text: parse, clamp, round to step.
TabMoves to the next field. The step buttons are skipped (tabIndex -1); the keys above cover them.
  • The input has type text, inputmode decimal (numeric when step is an integer and min is at least 0) and role spinbutton with aria-valuenow, aria-valuemin and aria-valuemax.
  • aria-valuetext is the value formatted with formatOptions and locale, for example 1.000 ₫, so screen readers say the same text the field shows.
  • Parsing: the group and decimal separators of the locale come from Intl.NumberFormat formatToParts. Group separators are stripped, the decimal separator maps to a dot, then the text is parsed. A string that does not parse sets the invalid state and does not call onValueChange.
  • Clearing the field calls onValueChange(null).
  • Step buttons are real buttons named by decrementLabel and incrementLabel, disabled at min and max, skipped by Tab (tabIndex -1) because the arrow keys cover them.
  • Layout: on a fine pointer the two buttons stack at the end of the field, increment on top. On touch (pointer: coarse) they sit side by side at the end, decrement then increment, each at least size-target-min (44px), with a color-border divider on each button's start edge so the touch area is visible.
  • Holding a step button repeats after a short delay; the announcement is the final value, not each step.
  • invalid sets aria-invalid; Field passes its error with aria-describedby.

Cài đặt

npx werewolf-ui add number-input

Chạy npx werewolf-ui init một lần trong app trước khi thêm component. Init cài token, thêm CSS import và luật cho agent. Add cài component này cùng các dependency và giữ nguyên theme của bạn. Xem Cài đặt để biết yêu cầu và cách cài qua registry.