DateRangePicker
A range of days to look at — the admin's costs, a report. A button with the range on it, opening the usual presets beside a calendar for days of one's own. Built from Popover, Calendar and Button. On a phone it opens as a bottom sheet.
Playground
Examples are pinned to October 2026, with today on Tuesday the 13th. A preset applies at once; picked days apply with Apply, and a single day picked is that day alone. Two months side by side, one in a phone's sheet; nothing after today can be picked.
this-month · 2026-10-01 → 2026-10-13import { DateRangePicker } from "@/components/shared/date-range-picker";
<DateRangePicker
presets={[
{ value: "7d", label: "Last 7 days" },
{ value: "this-month", label: "This month" },
…
]}
preset={range.preset}
range={{ from: range.from, to: range.to }}
today="2026-10-13"
onPreset={(preset) => …}
onRange={({ from, to }) => …}
/>On the button
A preset shows its name and, quieter, its days. Days of one's own show the days alone — the month once when they share it, the year only when it isn't this one.
7d · 2026-10-07 → 2026-10-13custom · 2026-10-05 → 2026-10-12custom · 2026-09-28 → 2026-10-04custom · 2020-03-01 → 2020-03-31preset="7d" → Last 7 days · 7–13 Oct
preset="custom" → 5–12 Oct
preset="custom" → 28 Sep – 4 Oct
preset="custom" → 1–31 Mar 2020On a phone, a sheet
Under sm the presets and calendar rise from the screen's foot as a bottom sheet (Sheet), as a Select's list does: the presets two to a row, 44px tall, over one month at full size, and Apply. Narrow this window to see it.
API
| Prop | Type | Default | Description |
|---|---|---|---|
| presets | { value: string; label: string }[] | — | The ranges a click away, in the order shown. |
| preset | string | — | The preset in use; any value not in presets (“custom”) means picked days. |
| range | { from: string; to: string } | — | The days in use, YYYY-MM-DD, both counted. |
| today | string | — | The last day there is to pick, YYYY-MM-DD; it also decides when a year is shown. |
| onPreset | (preset: string) => void | — | A preset was clicked. |
| onRange | ({ from, to }) => void | — | Days were picked and applied. |
| className | string | — | Layout only — w-full on a phone. |
Rules
- The URL holds the choice — a preset by name, so a bookmark of “This month” stays this month; picked days as two dates.
- Presets look back from today; nothing in the future can be picked.
- Days are the data's days: the admin's are UTC, as its ledger is.