1 slot open this monthBy the makers of shadcnregistryWe built shadcnregistry. Now we build your product.Book a callCheck availability
1 slot open this monthAI features?We build AI features into your product.Book a callCheck availability
1 slot open this month🚨 Slop police🚨 Slop police: we clean up vibe-coded apps.Report itReport your app
shadcnregistry
shadcnregistry
RegistryHealthInspect
Terms of ServicePrivacy Policy
  1. Registries
  2. pulld
  3. Number Range Input

Number Range Input

ui

Two number boxes that behave as one field: a low end, a high end, and a single verdict over the pair. Reach for it wherever a page asks for "from X to Y" in numbers: a price filter on a catalogue, search or marketplace listing page, a stock or quantity-on-hand filter in an admin table, a score, rating or review band, an age range on a form or audience targeting rule, a salary or compensation band on a job posting, the two thresholds on an alert or automation rule, a size, weight or dimension filter, a row-count or amount filter on a report, a latency, duration or response-time window on a dashboard query, a discount or margin band, a year or model-year range, a min/max pair that goes straight into a query string as ?min=&max=. Common asks it answers: "number range input react", "min max input react", "react min max number field", "price range input react", "shadcn price range filter", "shadcn min max input", "shadcn number range", "shadcn range input not slider", "min max filter component", "from to number input", "two number inputs one field", "range input with text entry", "type exact price range instead of slider", "price filter without slider", "shadcn slider vs range input", "open ended price range", "min only price filter", "1000 and up filter", "empty max means no upper limit", "empty number input becomes 0", "Number('') returns 0 bug", "cleared input treated as zero", "min greater than max validation react", "swap min and max automatically", "should I swap min and max or show an error", "validate range on blur not on change", "number input validation while typing is annoying", "field turns red while typing react", "cannot type decimal in controlled number input", "leading zero removed from input react", "decimal point disappears when typing react", "react controlled number input deletes what I type", "debounce range filter query", "do not fire query on every keystroke", "useSearchParams controlled input loses keystroke", "router push controlled input resets", "type number input discards invalid value", "input type number value empty string", "scroll wheel changes number input", "inputMode decimal", "fieldset legend two inputs", "aria-describedby two inputs one error", "accessible min max input", "screen reader range error", "aria-invalid which input", "thousands separator input parsing", "comma decimal separator input", "1,5 parsed as 15 bug", "数値 範囲 入力 react", "最小 最大 入力", "価格帯 フィルタ", "下限 上限 入力欄", "空欄を0にしない". Official shadcn/ui has no component of this kind, and the measurement is unusually clean: across its whole registry today (63 items listed, 62 fetched, 223,287 bytes of component source; `questionnaire` 404s on both styles) the words `minValue`, `maxValue`, `rangeStart`, `rangeEnd`, `fromValue`, `toValue`, `clampRange` and `lowerBound` return zero hits each, and so does `inputMode`. Its `slider` is the near neighbour and is genuinely close — it renders one thumb per value, so `value={[50, 500]}` does give you two — but it is a thing you drag inside a fixed track: there is no `<input>` to type an exact figure into, and a track cannot express an open end, so "1000 and up" is not a range it can hold. What you get: `NumberRangeInput`, the whole field — a `<fieldset>` named by its `<legend>`, the two boxes, the mark between them and one status line that carries either the error or a plain-language summary of the range ("50 to 500", "1,000 and up", "Up to 500"). Nothing is judged while you are typing: narrowing an upper end from 500 to 60 passes through 6, where the range is briefly crossed, so the verdict waits for a pause (`debounceMs`, 300ms) or for focus to leave the field, and `onCommit` fires on that same beat so a live query never goes out for the half-typed figure. `onValueChange` is the immediate one for parents that want per-keystroke state. Crossed ends are reported rather than quietly repaired, because swapping them the moment they cross takes the number you are halfway through typing and puts it in the other box; `crossBehavior="swap"` is there for filter bars that prefer it, waits until focus leaves, and announces that it swapped. An empty box is an open end and not a zero, so a one-sided range is valid and committable, and `requireBoth` turns that off where both really are needed. `bounds` sets the outer limits either end may take, inclusive, and is reported per end against the limit that was broken. `checkRange` and `parseBound` are exported as plain functions so the route handler receiving `?min=&max=` reaches the same verdict the field showed instead of carrying a second copy of the rules; `checkRange` returns issue codes rather than sentences, and every sentence the field can say is replaceable through `messages`. `parseBound` is deliberately stricter than `Number`: `Number("")` is 0, which is how clearing a filter turns into "zero and up", and `1e3`, `Infinity` and `0x10` all parse as numbers to it and as nothing to a price box. Group separators are accepted only where a group separator can go — `1,234,567` yes, `1,5` no — because stripping the character wherever it falls reads a comma one-and-a-half as 15, a bound wrong by a factor of ten with nothing on screen to suggest it; `decimalSeparator` says which convention is in force and is never guessed from the runtime locale, since `Intl` on the server and in the browser can disagree and a field that parses differently in the two passes is a hydration mismatch. The boxes are `type="text"` with `inputMode="decimal"`, not `type="number"`: a number input throws away text it cannot parse, so `input.value` is the empty string for `12e` and the reader's mistake vanishes instead of being explained, it steps its value when a scroll gesture passes over it while focused, and it refuses the half-typed `1.` this field has to accept. The status is attached to both inputs with `aria-describedby`, so the error reaches a screen reader from whichever box the reader is in, and which box is marked `aria-invalid` follows the problem's scope — a bad number marks that box, a crossed pair marks both, since neither number is wrong on its own. Tabbing between the two boxes does not settle the field, because that is movement inside it. Distinct from the neighbours: `number-input` is a single value with stepper buttons, `date-range-preset` is a range of dates chosen from named spans, `currency-input` and `duration-input` are single formatted values, and `filter-chips` shows and clears the filters a field like this one produces.

pnpm dlx shadcn@latest add "https://pulld.pages.dev/r/number-range-input.json"
Premium blocks for shadcn/uiShadcnblocks.com · AffiliateExplore
components/ui/number-range-input.tsx