Skip to content
WingoUI
ComponentsTemplatesDocsPricingAI agentsBlogChangelogContact
Components
Buttons & Actions
ButtonButton GroupCopy ButtonDelete ButtonTheme ToggleToggleToggle Group
Inputs
Avatar UploadCalendarCheckboxColor PickerComboboxCountry SelectCron PickerCurrency InputDate PickerDate Range PickerDate Time PickerDuration PickerEmail InputEmoji PickerFile UploadFilter BuilderInputInput GroupListboxMasked InputMention InputMonth PickerMulti SelectNumber InputOTP InputPassword InputPhone InputRadio GroupRatingSearch InputSegmented ControlSelectSignature PadSliderSwitchTag InputTextareaTime PickerTimezone SelectTree Select
Forms
Address InputBooking WidgetCSV ImportFieldForm WizardInline EditInvoice line itemsRich Text EditorSchema Builder
Navigation
Back To TopBounce SidebarBreadcrumbCommand MenuGlobal SearchGooey NavHook SidebarNav MenuNav TabsPaginationProximity SidebarScroll ProgressSidebarStepsTable Of ContentsTabsTopbarUser MenuWorkspace Switcher
Overlays
Alert DialogContext MenuDialogDrawerDropdown MenuFamily DrawerHover CardPopoverShare DialogSheetShortcuts DialogTooltipTour
Feedback
AlertBannerCookie ConsentEmoji ReactionEmpty StateNotification BellNotification CenterProgressRoute ProgressSetup ChecklistSkeletonSpinnerToast
Data Display
Approval FlowAvatarBadgeCalendar ViewCode BlockComment ThreadContact cardCountdownDescription ListDiff ViewerFile ListFile managerFolderGanttJSON ViewerKbdLog ViewerMarkdownPresenceSchedulerTagTimelineTree View
Tables & Lists
Audit LogData TableDeal PipelineEditable GridKanbanListMembers tablePermissions MatrixTableTask ListVirtual List
Charts & Stats
Animated CounterArea chartBar chartDonut chartFunnel chartGaugeGitHub ActivityHeatmap calendarLeaderboardLine chartRadar chartRadial barSparklineStat CardTrackerTrend badge
Layout
AccordionApp ShellCardCollapsibleDetail LayoutEntity HeaderPage HeaderScroll AreaSeparatorSettings LayoutSplit ViewToolbar
Media
Audio PlayerCarouselCSV ViewerDOCX EditorDOCX ViewerExcel ViewerFile SystemImage compareImage CropperImage GalleryImage LightboxLocation MapPDF ViewerPowerPoint ViewerQR CodeStep PlayerVideo Player
AI Kit
Agent Status CardChat MessageChat ThreadChat WidgetConversation ListFluid OrbGrid RevealMatrix OrbModel PickerPrompt InputReasoning IndicatorRule BuilderSuggestion ChipsTool Call TimelineUsage MeterVoice InputWorkflow Canvas
Text & Effects
Background PatternGravity LettersMarqueeTerminalText AnimateText Rotate
Mobile
Action SheetBottom Tab BarFABHaptic ButtonHero HeaderInstall PromptLarge Title HeaderNotification BannerOnboarding CarouselPicker WheelPull To RefreshSlide To ConfirmSticky CTA BarSwipe DeckSwipeable Tabs
Commerce
Cart drawerCategory pickerCheckout stepsCoupon inputFavorite buttonFilter panelListing cardListing gridOrder trackerPricePrice rangePricing tableProduct galleryQuantity stepperReviewsSeller card
Marketing Sections
App Download SectionBlog Cards SectionCTA SectionFAQ SectionFeature GridFeature ShowcaseHero SectionIntegrations SectionLogo CloudMarketing FooterMarketing HeaderMarketing SectionNewsletter SectionPricing SectionStats SectionSteps SectionTeam SectionTestimonials SectionTimeline SectionVideo Section
Blocks
Admin Dashboard PageAdmin ShellAI Chat PageAPI Keys PageAutomation Builder PageCheckout PageCRM Contact PageCRM Pipeline PageError PagesInbox PageInvoice PageKPI DashboardListing Detail PageLogin PageMarketplace Home PageMarketplace Search PageMarketplace ShellMobile App HomeOnboarding FlowOrder History PagePost Listing WizardPricing PageProfile PageRegister PageSettings PageUsers Admin Page
Templates
About PageAcademy PageAccessibility PageAffiliate PageAgency PageAI Product PageAppointment Booking PageBlog PageBlog Post PageBug Bounty PageCareers PageChangelog PageCommunity PageCompare PageContact PageCustomer Story PageCustomers PageDemo Booking PageDevelopers PageDocs Home PageDownload PageEnterprise PageEvents PageExperts PageFAQ PageFeature PageGlossary PageHelp Article PageHelp Center PageHome Services PageImpact PageIntegration Detail PageIntegrations PageJob Detail PageLanding PageLaunch PageLegal PageManifesto PagePortfolio PagePress PageProduct Tour PagePromo PageReal Estate PageReferral PageResearch Report PageRestaurant PageRoadmap PageROI Calculator PageSecurity PageSolutions PageStartups PageStatus PageStorefront PageSwitch PageTemplate Gallery PageTravel PageWaitlist PageYear in Review Page
Hooks & Utilities
AI ChatChart kitCommerceDate utilsFile exportFile typesFiltersForm validationHapticsInvoicingMotion presetsNavigationNumber formatRelative timeRomanian locationsSpreadsheetText matchTonesUIProvideruseClipboarduseConsentuseControllableStateuseCooldownuseDebounceuseKeyboardInsetuseLocaleuseLongPressuseMediaControlsuseMediaQueryuseOverflowItemsusePersistedStateuseScrollEdgesuseScrollStateuseSelectionuseShortcutuseStickToBottom
GalleryTheme

Commerce

Price range

Commerce

Price range React component for Tailwind CSS v4

PricePricing table
Live props
Content
min
max
countHidden
currency
locale
label
description
Behavior
stepAuto
scale
Appearance
bins24
size
tone
color
Layout
showHistogram
showInputs
State
error
disabled
Code
import { PriceRange } from "@/components/ui/price-range"

<PriceRange />
  1. Home
  2. Components
  3. Commerce
  4. Price range
Pro

Price range React component for Tailwind CSS v4

v1.0.0·Added in 1.0.0

A React price range filter: a histogram of the results over a two-thumb slider, synced min and max inputs, preset chips and a live count.

Drag either thumb along the histogram: the bars inside the range light up, the thumb shows its price in a bubble and the inputs follow, snapped to a round step. Drag a thumb all the way to an end to open that bound (the input empties to its Min or Max placeholder). On a phone the thumbs have 44px hit areas and a vertical swipe over the slider still scrolls the sheet it sits in. Type in Min or Max for an exact amount, even one beyond the slider's ends (the thumb stays pinned at the end); Enter or leaving both fields applies it, and a min above the max swaps. Tap a preset chip to set both ends at once, tap it again to clear the filter; on phones the chips scroll sideways. Keyboard: Tab to a thumb, Left / Right or Up / Down move by one step, Page Up / Page Down (or Shift with an arrow) by ten, Home and End jump the thumb to the start or the end of the track but never past the other thumb (Home on the minimum and End on the maximum open that bound); arrows move between preset chips and Space selects one. The count under the filter changes with a short fade and is read once by screen readers. Under reduced motion the bars appear without growing.

Price range is a Pro React component in the React commerce components category, written in TypeScript for React 19 and Tailwind CSS v4. It installs as source with npx wingo-ui@latest add price-range, together with Slider, Currency Input, Tag, Field, Commerce and 6 more and the npm package motion.

Add to a project

Requirements: React 19 and Tailwind CSS v4, in Next.js (App Router or Pages), Vite or React Router. Installation guide · Next.js setup · Vite setup

npx wingo-ui@latest add price-range

Price range is part of Pro, with 298 more Pro items: $8 a month or $150 once. 14-day refund before you download any Pro item. See plans

Usage

Import PriceRange from @/components/ui/price-range.

import { useState } from "react"
import { PriceRange, type PriceRangeValue } from "@/components/ui/price-range"

const PRICES = [320, 450, 890, 1150, 1200, 1490, 1650, 2100, 2450, 2900, 3400, 4300, 5200, 7800]

export function PriceFilter({ onApply }: { onApply: (value: PriceRangeValue) => void }) {
  const [value, setValue] = useState<PriceRangeValue>([null, 3000])
  const count = PRICES.filter((p) => (value[0] === null || p >= value[0]) && (value[1] === null || p <= value[1])).length
  return (
    <PriceRange
      label="Price"
      value={value}
      onValueChange={setValue}
      onValueCommit={onApply}
      min={0}
      max={8000}
      prices={PRICES}
      presets={[
        { min: null, max: 500 },
        { min: 500, max: 1500 },
        { min: 1500, max: null },
      ]}
      count={count}
      labels={{
        results: (n, formatted) => (n === 1 ? "1 listing" : `${formatted} listings`),
      }}
    />
  )
}

Props

  • value
    PriceRangeValue

    The controlled range, [min, max] in major units (dollars). null is an open end: [null, 3000] means under $3,000. A value outside the slider pins its thumb to the end while the inputs keep the real number. The type is exported as PriceRangeValue ([min: number | null, max: number | null]), shared with filter-panel.

  • defaultValuedefault "[null, null]"
    PriceRangeValue

    The starting range while uncontrolled. The default leaves both ends open.

  • onValueChange
    (value: PriceRangeValue) => void

    Fires while dragging (at most once per frame), while typing and on presets. Use it for the live readout.

  • onValueCommit
    (value: PriceRangeValue) => void

    Fires when a thumb is released or moved by a key, when an input is left or confirmed with Enter, and when a preset is tapped. Fetch results here. A min above the max arrives swapped.

  • mindefault 0
    number

    The slider's low end. A thumb dragged here opens the lower bound.

  • maxdefault 10000
    number

    The slider's high end. A thumb dragged here opens the upper bound; prices above it count in the last bar. Equal to min it becomes a single price with locked thumbs.

  • stepdefault 0
    number

    The price step thumbs, arrows and inputs snap to. 0 picks a round one for the range (1, 5, 10, 50, 100, 500...), about a hundredth of it; on a log scale it grows with the price. Currencies without decimals always step in whole units.

  • scaledefault "linear"
    "linear" | "log"

    log spreads cheap prices out and squeezes the expensive tail, for wide ranges like real estate ($100 to $500,000). The histogram's buckets follow the same scale.

  • prices
    readonly number[]

    Raw prices of the results, counted into the histogram. To send histogram facets from the server instead, count them there with binPrices (see Parts).

  • histogram
    readonly number[]

    Precomputed bucket counts (server facets), one per equal-width bucket of the track (log-spaced on a log scale). Wins over prices.

  • binsdefault 24
    number

    How many bars prices are binned into. Empty buckets leave a gap.

  • showHistogramdefault true
    boolean

    The bars above the slider. Without prices or a histogram there is nothing to draw and it stays hidden.

  • showInputsdefault true
    boolean

    The Min and Max currency inputs under the slider.

  • presets
    PriceRangePreset[]

    Chips that set both ends at once: PriceRangePreset = { label?: ReactNode; min: number | null; max: number | null }, e.g. { min: null, max: 500 }. A chip without a label reads labels.under / between / over ("Under $500", "$500 to $1,500"). It stays selected while the value matches it.

  • countdefault 0
    number

    Results in the current range, shown under the filter as labels.results ("832 results") in a polite live region. 0 hides it.

  • currencydefault "USD"
    string

    ISO 4217 code for the inputs, the bubbles and the chips: "$", "€", "lei".

  • localedefault ""
    string

    Formats every price ("$1,500" in en-US, "1.500 $" in de-DE). Empty uses UIProvider, then the page.

  • labeldefault ""
    ReactNode

    The field label above the histogram; it names the whole group.

  • descriptiondefault ""
    ReactNode

    Helper text under the filter, like "per month".

  • errordefault ""
    ReactNode

    Replaces the description and marks both inputs invalid.

  • sizedefault "md"
    "sm" | "md" | "lg"

    Scales the slider (4 / 6 / 8px track), the histogram (40 / 48 / 56px), the inputs (32 / 40 / 48px, 44px on touch for md) and the chips together.

  • tonedefault "neutral"
    "neutral" | "brand" | "success" | "warning" | "danger" | "info"

    Colors the slider's range, the lit bars (at 55%) and the selected chip. neutral is black in light mode and white in dark mode.

  • colordefault ""
    string

    Any CSS color; replaces the tone everywhere. Leave empty to use the tone.

  • disableddefault false
    boolean

    Dims the filter and blocks the thumbs, inputs and chips, e.g. while results load.

  • labels
    Partial<PriceRangeLabels>

    Every string, English by default: min "Min", max "Max", any "Any", thumbMin "Minimum price", thumbMax "Maximum price", under (p) => `Under ${p}`, over (p) => `Over ${p}`, between (a, b) => `${a} to ${b}`, results (n, formatted) => `${formatted} results`, presets "Price presets". Pass only the keys you translate.

  • classNames
    Partial<Record<"histogram" | "bar" | "slider" | "inputs" | "input" | "presets" | "count", string>>

    Extra classes for the inner parts. className stays on the root (the field wrapper when a label, description or error is set).

Parts

  • binPrices

    function

    binPrices(prices, { min?, max?, bins?, scale? }) returns the histogram counts as number[]: equal-width or log-spaced buckets, prices outside min..max counted in the first or last one. The same counting the component does, for facets computed on the server.

  • DEFAULT_PRICE_RANGE_LABELS

    constant

    The English copy: min "Min", max "Max", any "Any", the thumb names, under / over / between preset text and the results count.

  • PriceRangePreset

    type

    One preset chip: { label?, min, max }, with null for an open end. Without a label the chip reads Under, Over or between.

Examples

In a filter sheet

A bottom sheet keeps a draft; the footer shows the live count and applies it.

Real estate in EUR

A log scale from €20,000 to €500,000 with a step of €1,000.

Rent per month

$500 to $5,000 with a caption and no histogram.

Narrow sidebar

Slider and inputs only, size sm, in a 256px filter column.

Open ends

Under $3,000 and over $1,000: one bound left open.

Sizes in the brand tone

sm, md and lg with tone="brand" lighting the bars and the range.

Disabled while loading

Each commit locks the filter until the results come back.

Dependencies

motionslidercurrency-inputtagfieldcommercenumber-formatuse-localeuse-controllable-statemotiontonesui-config

Files

  • components/ui/price-range.tsx

FAQ

Is the React Price range component free?

The preview, props and docs are free; copying the source needs a Pro plan. See plans.

How do I install Price range?

Run npx wingo-ui@latest add price-range. It also adds the Wingo UI items it builds on (Slider, Currency Input, Tag, Field, Commerce and 6 more) and the npm package motion. With shadcn, add the @wingo-ui registry with your API key, then run npx shadcn@latest add @wingo-ui/price-range.

How does Price range behave on phones?

On a phone the thumbs have 44px hit areas and a vertical swipe over the slider still scrolls the sheet it sits in. Tap a preset chip to set both ends at once, tap it again to clear the filter; on phones the chips scroll sideways.

See also

Filter panelSliderCurrency InputPriceListing grid

Used in

Filter panelMarketplace Search PageReal Estate Page

More React commerce components

Listing cardProduct galleryFavorite buttonOrder trackerCheckout stepsCart drawer
All 16 React commerce components

Articles using this component

  • Nextjs Real Estate Template: Search, Map and Mortgage

    Build it: screens and apps · Nov 6, 2026 · 10 min read

PreviousPriceNext Pricing table

Electronics › Mobile phones

Filter by price

Any
$MinAmount in US dollars
–
$MaxAmount in US dollars
400 listings400 listings

onValueCommit ×0 [null, null]