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

Inputs

Date Range Picker

Inputs

Date Range Picker React component for Tailwind CSS v4

Date PickerDate Time Picker
Live props
Field
label
description
error
required
invalid
disabled
readOnly
size
variant
radius
name
Content
placeholder
locale
presets
showDuration
weekStartsOn-1
sheetTitle
Layout
numberOfMonths2
side
align
Behavior
minDaysNo limit
maxDaysNo limit
excludeDisabled
confirm
clearable
responsive
Appearance
tone
color
State
defaultOpen
Code
import { DateRangePicker } from "@/components/ui/date-range-picker"

<DateRangePicker />
  1. Home
  2. Components
  3. Inputs
  4. Date Range Picker
Pro

Date Range Picker React component for Tailwind CSS v4

v1.0.0·Added in 1.0.0

A React date range picker for reports and bookings: two months with presets on desktop, a one-month bottom sheet on phones.

Click the field to open a panel under it: presets on the left (Last 7 days, This month, Last quarter...), two months on the right and a summary with Cancel and Apply at the bottom. The first click on a day sets the start, the band follows the pointer as a preview, and the second click sets the end (a day before the start swaps them); days that would break minDays, maxDays or cross a disabled day are unavailable while you pick the end. A preset fills both ends and moves the calendar to it. The summary under the calendar updates as you go (the dates, then "· 7 days"), and Apply commits; Cancel, Escape or a click outside throw the draft away, so a half-picked range never reaches the value. With confirm={false} the range commits as soon as both ends are picked, or on a preset. The clear button in the field empties it. Keyboard: Enter, Space or ArrowDown on the field open the panel and move focus into the calendar (the start of the range, else today); arrows move a day or a week, Home / End jump to the week's ends, PageUp / PageDown move a month (Shift: a year), Enter picks the start and then the end; Tab reaches Cancel and Apply, Shift+Tab the presets (arrow keys move between presets); Ctrl+Enter (Cmd+Enter on a Mac) applies from anywhere in the panel; Escape cancels and returns focus to the field. On a phone the field opens a bottom sheet with the presets as a row of chips, two Start / End cells (the end being picked is outlined), one month you can swipe, and Apply / Cancel pinned above the home indicator; dragging the sheet down cancels.

Date Range Picker is a Pro React component in the React input components category, written in TypeScript for React 19 and Tailwind CSS v4. It installs as source with npx wingo-ui@latest add date-range-picker, together with Button, Calendar, Drawer, Field, Date utils and 6 more and the npm packages @radix-ui/react-popover, date-fns, motion and lucide-react.

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 date-range-picker

Date Range Picker 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 DateRangePicker from @/components/ui/date-range-picker.

import { useState } from "react"
import { DateRangePicker, type DateRange } from "@/components/ui/date-range-picker"

export function SalesReport() {
  const [range, setRange] = useState<DateRange>({ from: null, to: null })

  return (
    <DateRangePicker
      label="Period"
      name="period"
      locale="en-US"
      placeholder="Pick a period"
      max={new Date()}
      value={range}
      onValueChange={setRange}
      presetLabels={{ lastQuarter: "Previous quarter" }}
      labels={{ duration: (days) => (days === 1 ? "1 day" : `${days} days`) }}
    />
  )
}

Props

  • labeldefault ""
    ReactNode

    Renders the field anatomy around the trigger. A string label also titles the phone sheet and names the panel. A click on the label focuses the trigger without opening it.

  • descriptiondefault ""
    ReactNode

    Helper text under the field, linked with aria-describedby. The error takes its place while it shows.

  • errordefault ""
    ReactNode

    Replaces the description with the animated error and marks the field invalid (red ring, aria-invalid on the trigger).

  • requireddefault false
    boolean

    The mark after the label and aria-required on the trigger.

  • invaliddefault false
    boolean

    The invalid look and aria-invalid without a message.

  • disableddefault false
    boolean or DateMatcher or DateMatcher[] (DateMatcher: Date, Date[], { from, to }, { before }, { after }, { dayOfWeek } or (date) => boolean)

    true dims the field, blocks it and leaves the hidden inputs out of the submit. A DateMatcher instead disables those days in the calendar: a Date, a list, { from, to }, { before }, { after }, { dayOfWeek: [0, 6] } or a (date) => boolean test (booked nights, holidays).

  • readOnlydefault false
    boolean

    The value stays visible and the trigger focusable, but it never opens and the clear button hides.

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

    The field box: 32 / 40 / 48px (md is 44px on touch screens), like every other field.

  • variantdefault "soft"
    "soft" | "outline" | "underline" | "ghost"

    The box look, shared with every field.

  • radiusdefault "md"
    "none" | "sm" | "md" | "lg" | "full"

    The box corners, per size like the button (8 / 10 / 12px at md). Ignored by underline.

  • placeholderdefault "Select dates"
    string

    The trigger text while no range is set.

  • localedefault ""
    string

    Month and weekday names, digits, the first day of the week and the trigger text. Empty: the nearest LocaleProvider, else the browser.

  • displayFormat
    Intl.DateTimeFormatOptions

    How the trigger and the summary write the range, through Intl formatRange: { day: "numeric", month: "short", year: "numeric" } gives "1 to 30 sept. 2026" joined by an en dash; only a start gives the start followed by a dash.

  • numberOfMonthsdefault 2
    1 | 2 | 3

    Months side by side on desktop. The panel drops months that do not fit the screen; phones always show one.

  • presetsdefault true
    boolean | (RangePresetKey | DateRangePreset)[]

    true: Last 7 days, Last 30 days, This month, Last month, This quarter, Last quarter, This year. Or a list mixing built-in keys ("today", "yesterday", "last90Days", "thisWeek", "lastWeek", "lastYear", "yearToDate"...) and your own { label, value: DateRange | () => DateRange }. Presets that run past min / max are cut to them; fully outside, or longer than maxDays, they are disabled.

  • presetLabels
    Partial<Record<RangePresetKey, string>>

    Labels for the built-in presets (English by default).

  • min
    Date

    Earlier days are disabled and the calendar stops at this month.

  • max
    Date

    Later days are disabled (reports pass today) and the calendar stops at this month.

  • minDaysdefault 0
    number

    The shortest range in days, both ends counted (a 2 night stay is 3 days). Closer ends are unavailable while picking.

  • maxDaysdefault 0
    number

    The longest range in days, both ends counted. Further ends are unavailable while picking, longer presets are disabled.

  • excludeDisableddefault true
    boolean

    A range can not span a disabled day: past the first one, the days are unavailable while picking the end (bookings).

  • confirmdefault true
    boolean

    Apply / Cancel buttons; the value only changes on Apply. false commits and closes as soon as both ends are picked, or on a preset.

  • clearabledefault true
    boolean

    A clear button in the field while a range is set.

  • showDurationdefault true
    boolean

    The summary shows the length ("7 days", or nights with labels.nights).

  • labels
    { apply?; cancel?; clear?; start?; end?; pickStart?; pickEnd?; presets?; duration?: (days) => string; nights?: (nights) => string }

    Every string of the panel: { apply: "Apply", cancel: "Cancel", clear: "Clear", start: "Start", end: "End", pickStart: "Pick a start date", pickEnd: "Pick an end date", presets: "Presets", duration: (d) => d + " days" }. nights switches the summary to nights (a 4 day stay is 3 nights).

  • weekStartsOndefault -1
    0 | 1 | 2 | 3 | 4 | 5 | 6 | -1

    0 Sunday ... 6 Saturday; -1 follows the locale (Monday in ro-RO, Sunday in en-US).

  • events
    CalendarEvent[]

    Dots under days in the calendar: { date, tone?, color?, label? }.

  • calendarProps
    Partial<CalendarProps<"range">>

    Anything else for the calendar: size, radius, strikeDisabled, showWeekNumbers, renderDay, labels (Romanian month navigation), fixedWeeks (on by default so the panel never changes height).

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

    The color of the picked ends, the band, the active preset check, the outline of the cell being picked and the Apply button.

  • colordefault ""
    string

    Any CSS color; replaces the tone. Text on the fill turns black or white by lightness.

  • icon
    ReactNode

    The leading icon of the field (CalendarRange by default).

  • defaultOpendefault false
    boolean

    Starts open while uncontrolled.

  • value
    DateRange

    The committed range, controlled: { from: Date | null; to: Date | null }. A value with only from shows the start followed by a dash; a backwards one is shown in order.

  • defaultValue
    DateRange

    The starting range while uncontrolled; { from: null, to: null } by default. A form reset returns to it.

  • onValueChange
    (range: DateRange) => void

    Called on Apply, on a clear, or as soon as both ends are picked with confirm={false}. Never with a half-picked draft.

  • open
    boolean

    The panel or sheet, controlled. Pair it with onOpenChange. Every opening starts from the committed value.

  • onOpenChange
    (open: boolean) => void

    Called when the panel opens or closes.

  • responsivedefault true
    boolean

    A bottom sheet below 768px instead of the popover.

  • sheetTitledefault "Select dates"
    string

    The sheet title and the panel name when the label is not plain text.

  • sidedefault "bottom"
    "top" | "right" | "bottom" | "left"

    Where the panel opens; it flips when there is no room.

  • aligndefault "start"
    "start" | "center" | "end"

    The panel's alignment against the field.

  • container
    HTMLElement | null

    Portal target for the panel and the sheet (document.body by default).

  • namedefault ""
    string

    Renders hidden inputs name.from and name.to with ISO dates ("2026-09-23", local, never UTC).

  • id
    string

    The trigger's id (generated by default); the label points at it.

  • fieldProps
    Partial<FieldProps>

    Anything else for the field wrapper: optional, labelAction, orientation, labelWidth, hideLabel, className.

  • classNames
    Partial<Record<DateRangePickerSlot, string>>

    Classes for the parts: field, label, description, error, counter, box, trigger, icon, value, clear, chevron, content, presets, preset, calendar, footer, summary, cell (the phone Start / End cells), actions. className goes on the field root (or the box without a label).

  • ref
    RefObject<HTMLButtonElement | null> or (node) => void

    The trigger button; the rest of the props (onBlur, aria-*, data-*) land on it too, so react-hook-form can focus and touch it.

Parts

  • DateRangePreset

    type

    One preset button: { label, value: DateRange | (() => DateRange), key? }.

Examples

Booking a stay

Booked nights are disabled and struck through, minDays 3 (2 nights), labels.nights counts nights and the price follows.

Instant filter

confirm={false}: the list filters as soon as both ends are picked, or on a preset tap. Mixed built-in preset keys.

Compact

numberOfMonths 1 with a small outline field and small calendar cells, for a sidebar or a toolbar.

Custom presets

Your own ranges, computed when the panel opens: this weekend, next week, the next 30 days. Brand tone for the selection.

Maximum length

maxDays 31: ends further out are unavailable while picking, and presets longer than that are disabled.

Locales

The same labels in two locales: ro-RO starts weeks on Monday and writes the day before the month; en-US starts on Sunday and writes the month first.

Sizes and variants

sm soft, md outline, lg underline, a ghost field and a disabled one: the same box as every other field.

Dependencies

@radix-ui/react-popoverdate-fnsmotionlucide-reactbuttoncalendardrawerfielddate-utilsuse-controllable-stateuse-localeuse-media-querymotiontonesui-config

Files

  • components/ui/date-range-picker.tsx

FAQ

Is the React Date Range Picker component free?

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

How do I install Date Range Picker?

Run npx wingo-ui@latest add date-range-picker. It also adds the Wingo UI items it builds on (Button, Calendar, Drawer, Field, Date utils and 6 more) and the npm packages @radix-ui/react-popover, date-fns, motion and lucide-react. With shadcn, add the @wingo-ui registry with your API key, then run npx shadcn@latest add @wingo-ui/date-range-picker.

How does Date Range Picker behave on phones?

A bottom sheet below 768px instead of the popover. The responsive prop controls this and is on by default.

See also

Date PickerCalendarFieldDrawerDate utilslibuseLocalehook

Used in

Data TableFilter BuilderAudit LogAdmin Dashboard PageCRM Pipeline PageKPI DashboardUsers Admin PageTravel Page

More React input components

CheckboxComboboxCountry SelectCron PickerCurrency InputDate Time Picker
All 40 React input components

Articles using this component

  • React Month Picker: Months, Quarters and Ranges

    Component guides · Oct 19, 2026 · 8 min read

  • React Date Range Picker With Presets and a Phone Sheet

    Component guides · Oct 13, 2026 · 9 min read

PreviousDate PickerNext Date Time Picker

Sales report

Wildflower Studio · Chicago

Store
All stores

Sales

$21,335

Orders

144

Average order

$148

Sales: $21,335.29 · 144 orders · 30 days

period.from=2026-10-09period.to=2026-11-07phone: a “Select dates” sheet · no limit · excludeDisabled