Home

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-pickercomponents/ui/date-range-picker.tsx

Use case

For the AI

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
A report: named periods, the last N, any range, compared
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.