DateRangePicker
v1.0.16Updated 19 Oct 2026Two-month range picker in a Popover, with an optional quick-range rail (Today, Yesterday, Last 7/30 days, This month, Last month, This quarter) via `presets` — pass `true` for the built-ins or supply your own presets array. The trigger collapses the range to a compact label, dropping the repeated year when both ends share one. Supports Buddhist Era (พ.ศ.) and Thai locale labels.
Preview
Live · 11 stories▸Why this is recommendeddaterangedaterangepickerpicker+6
DateRangePicker — date range.
- ✓Picking a reporting period or date range
- ✓Filtering a dashboard by last 7 / 30 days
- ✓Selecting a billing period
- ✓Choosing check-in and check-out dates
- ✓Analytics date-range filter with quick presets
- ✓Thai-localised reporting window in Buddhist Era (พ.ศ.)
- ✓Exporting records between two dates
- Start/end range selection across configurable numberOfMonths (default 2)
- Built-in quick-range presets rail (presets={true})
- Custom presets array with click-time range factories
- Smart range label that omits the duplicated year
- fromDate / toDate constraints
- Controlled (range) or uncontrolled (defaultRange)
- Thai Buddhist Era calendar + Thai locale labels
- BE/CE era toggle chip — the choice STICKS across reopens, the trigger label follows it, and it is readable/settable via calendarMode (fb_20a80f6c)
- Does NOT pull in Select
- i18n: calendar month/weekday names and year digits follow the app LocaleProvider (useB69Locale) for non-Thai locales
- Labelled by FormField: id, aria-describedby and aria-invalid land on the trigger button (its label names it) and the trigger fills the field column (standalone w-72)
- ✕Picking one single date (use DatePicker)
- ✕Picking a date plus a time (use DateTimePicker)
- ✕Inline month view (use Calendar directly)
Install
Ask your agent, or call the MCP tool directly. The resolved bundle is written into your project.
Usage
Tick variants in the left menu — each adds a tab with that variant’s real story source, the same code the preview above renders.
Props
rangeSelected range (controlled), from react-day-picker
—onRangeChangeRange selection callback
—defaultRangeInitial value for uncontrolled usage (ignored when `range` is provided)
—presetsQuick-range rail; `true` uses the built-in set (Today … This quarter). Picking one commits the range and closes the popover
—numberOfMonthsMonths shown side by side (default 2)
—placeholderTrigger text when no range is selected (default "Pick a date range")
—fromDateMin selectable date
—toDateMax selectable date
—formatStrdate-fns format string (default "MMM d, yyyy")
—alignPopover alignment
—childrenFooter content rendered under the calendar
—calendarThe era the field shows by DEFAULT (default "gregory"); "buddhist" renders พ.ศ. years and puts the era chip in the footer. "gregorian" is the deprecated spelling — still accepted and normalised on arrival, so a host passing its own stored Intl calendar value through no longer gets TS2322 and has to translate the word by hand (fb_e8de80fb). The `data-calendar` attribute still writes "gregorian", because consumers already style and test against it
—calendarModeWhich era the picker is SHOWING, as a controlled value. Pair with `onCalendarModeChange` to remember the user's choice across opens or sessions. Left alone the picker keeps its own, seeded from `calendar` — and no longer resets it when the popup reopens, which is what stopped the era chip from sticking (fb_20a80f6c). Presentation only: the Date handed back is the same Date in either era. The deprecated "gregorian" spelling is accepted here too and normalised on arrival, so an era a host stored under the old word can be fed straight back in without a type error (fb_e8de80fb); `onCalendarModeChange` always reports the house spelling
—onCalendarModeChangeFires with the era the user switched the picker to
—showEraChipShow the era chip even when `calendar` is gregorian, so a ค.ศ. field can be switched to พ.ศ. (default false — a chip appearing in footers that never had one is a change other consumers did not ask for)
—localeLocale for labels and formatting. `locale="th"` now also supplies the Thai BUILT-IN strings (placeholder, era chip พ.ศ./ค.ศ., and for DateRangePicker the whole quick-range rail) — previously the grid localised but the trigger stayed English, which only showed on an EMPTY field. An explicit `labels` (or `placeholder`) still wins, and locale="en" is unchanged. MCP feedback fb_3adc3df4.
—labelsOverride the built-in UI strings (i18n): placeholder, presetsLabel, presetToday, presetYesterday, presetLast7Days, presetLast30Days, presetThisMonth, presetLastMonth, presetThisQuarter, be, ce, toggleCalendarEra. The three era keys are forwarded to the era chip, so the พ.ศ./ค.ศ. text and the chip aria-label are reachable without touching the quick-range rail. Merged over the locale defaults (DEFAULT_DATE_RANGE_PICKER_LABELS, or TH_DATE_RANGE_PICKER_LABELS under locale="th"), so a partial object only replaces the keys it names
—disabledDisables the trigger
—invalidMark the field invalid (destructive ring); aria-invalid wins when both are set
—idDOM id set on the trigger button; FormField clones its field id in, and its label then names the control
—aria-labelAccessible name of the trigger when there is no visible label
—aria-labelledbyId(s) of the element(s) that label the trigger; wins over a FormField label
—aria-describedbyId(s) of hint or error text describing the trigger (FormField clones this in)
—aria-invalidMark the trigger invalid; FormField clones this in from its error prop
—aria-requiredAccepted (FormField clones it in) but not rendered: ARIA does not allow it on a button. The label text "required" is part of the name instead
—