Date range picker
A pill that says a period in words and opens named periods, "the last N", two typed ends and two months to pick any range from.
ui.date-range-picker
components/ui/date-range-picker.tsxUse case
Use it wherever a page is read for a period: a sales report, an activity log, a dashboard's figures, an accountant's export, the numbers of a campaign. The pill reads "Last 30 days" or "This month"; it opens a rail of named periods (each showing its dates), "Last [N] [days, weeks, months]", the start and end typed ("oct 3", "3 octobre") or clicked on two months, and Cancel / Apply. A report that can show the period before passes `onCompareChange` and gets "Compare to: nothing, previous period, last year".
Use it when
- A report or a log read for a period, kept in the address (`?period=last-30-days`)
- Any custom range of days, typed or clicked
- A figure compared to the period before or to last year
- Choosing among the finished periods an export covers (pass `presets`, `max`, `maxDays`)
Use something else when
- One day in a form (a due date, a start day): Date picker
- Two or three fixed views a page switches between at once, with no custom range: Segmented control
- The month itself belongs on the page (a schedule's side panel): Calendar
Examples
Oct 1–30, 2026
En français, une petite taille
Only finished periods, no all time, a year at most
Code
Import
import { useState } from "react"
import { DateRangePicker } from "@/components/ui/date-range-picker"
import { resolveRange } from "@/lib/date-range"Usage
export function SalesPeriod({ today }: { today: string }) {
const [period, setPeriod] = useState("last-30-days")
const range = resolveRange(period, today) // { from, to }, or null for all time
return <DateRangePicker value={period} onValueChange={setPeriod} today={today} max={today} />
}Rules
- The value is a key of `lib/date-range.ts` ("this-month", "last-30-days", "2026-09-01..2026-09-30", "all"), never frozen dates: keep the key in the address and resolve it on the server with `resolveRange(key, today)`, so "This month" stays this month.
- Pass `today` as the tool's own day (`YYYY-MM-DD` in its time zone); the browser's day is only the fallback.
- Nothing changes until Apply: the rail, the typed ends and the calendar only move the draft.
- Leave "all" out of `presets` where a page cannot read everything; cap a heavy read with `maxDays` and future days with `max`.
- Show `onCompareChange` only on a page that really draws the comparison; it is a segmented control, never option cards.
- French by default (`lang="fr"`), English with `lang="en"`; the words are the piece's own.