- Accordion
- Action Bar
- Action Toast
- Alert Banner
- Alert Dialog
- Alert
- Alphabet Index
- Amount Field
- Aspect Ratio
- Attachment
- Availability List
- Avatar
- Badge
- Bank Card
- Breadcrumb
- Bubble
- Button Group
- Button
- Calendar
- Camera Frame
- Card Design Picker
- Card Details
- Card Peek
- Card Stack
- Card
- Carousel
- Chart
- Chat Thread
- Checkbox
- Chip Group
- Choice Tile
- Code Block
- Code Dots
- Collapsible
- Combobox
- Command
- Context Menu
- Copy Field
- Countdown
- Country Select
- Data Table
- Date Field
- Date Picker
- Date Range Picker
- Dialog
- Direction
- Document Paper
- Drawer
- Dropdown Menu
- Empty
- Entity Avatar
- Field
- File Trigger
- File Upload Item
- Hover Card
- Input Group
- Input OTP
- Input
- Item
- Kbd
- Key Value
- Keypad
- Label
- Live Region
- Marker
- Message Scroller
- Message
- Money Amount
- Native Select
- Number Field
- Pagination
- Percent Split Editor
- Phone Field
- Popover
- Progress
- QR Code
- Questionnaire
- Radio Group
- Range Histogram
- Rating
- Resizable
- Scroll Area
- Search Field
- Select
- Separator
- Share Bar
- Sheet
- Sidebar
- Skeleton
- Slider
- Sonner
- Sortable List
- Spinner
- Status Timeline
- Step Header
- Steps List
- Swipe Row
- Switch
- Table
- Tabs
- Text Field
- Textarea
- Tier Ladder
- Toggle Group
- Toggle
- Token Search Field
- Tooltip
- Trace
- Transaction Row
- Usage Meter
"use client"
import {The DateRangePicker component picks a period with a start and an end date. Use it for statement periods, transaction filters, exports, and trip dates. Both ends can be typed as segments or picked on a range calendar, which opens in a popover on wide screens and a bottom sheet on phones. Pick a Date Picker for a single day, and a Date Field for one date far from today, such as a date of birth.
Installation#
pnpm dlx shadcn@latest add @dots/date-range-picker
Usage#
import { CalendarDate } from "@internationalized/date"
import { DateRangePicker } from "@/components/ui/date-range-picker"<DateRangePicker
label="Statement period"
placeholderValue={new CalendarDate(2026, 11, 1)}
/>Composition#
DateRangePicker is ready to use as is. To change the layout, compose the styled root with the parts from Date Picker:
DateRangePickerRoot
├── DatePickerLabel
├── DatePickerField
│ ├── DatePickerInput slot="start"
│ ├── DateRangePickerSeparator
│ ├── DatePickerInput slot="end"
│ └── DatePickerButton
├── DatePickerDescription
├── DatePickerError
└── DatePickerOverlayContent overlay="auto" title="Statement period"
├── DatePickerPresets (optional)
└── RangeCalendarDatePickerOverlayContent picks the overlay: a popover (DatePickerContent) on wide screens and a bottom sheet (DatePickerSheetContent) on phones. Use DatePickerContent alone only when you want the popover everywhere.
Features#
- Start and end dates typed as segments in the order and format of the locale
- A range calendar in a popover on wide screens and a bottom sheet on phones
- Preset chips such as "Last 7 days" or "This month" above the calendar
- One or more months side by side in the popover, always one in the sheet
- Limits with
minValue,maxValue, andisDateUnavailable - Form submission with
startNameandendName - Label, description, and error message wired up for assistive tech
- A default field and a small pill for filter rows
Presets#
Pass presets to show quick ranges above the calendar. A function value gets today in the user's time zone and runs only when the chip is pressed, so nothing reads the clock while rendering. A fixed value also lights its chip when the same days are picked by hand.
"use client"
import { CalendarDate, startOfMonth } from "@internationalized/date"const presets: DateRangePreset[] = [
{
id: "month",
label: "This month",
value: (today) => ({ start: startOfMonth(today), end: today }),
},
{
id: "q3",
label: "Q3 2026",
value: {
start: new CalendarDate(2026, 7, 1),
end: new CalendarDate(2026, 9, 30),
},
},
]Months#
Use numberOfMonths to show more than one month in the popover. The sheet on phones always shows one.
"use client"
import { CalendarDate } from "@internationalized/date"Sheet#
The calendar opens in a popover on wide screens and a bottom sheet on phones. Set overlay to "sheet" or "popover" to use one on every screen. The sheet heading is the label unless you pass sheetTitle.
"use client"
import { CalendarDate } from "@internationalized/date"Size#
size="default" matches Input. size="sm" is a pill for filter rows and toolbars. Without a visible label, pass an aria-label.
"use client"
import { CalendarDate } from "@internationalized/date"Limits#
Set minValue and maxValue to keep the period inside the account history. Days outside the limits can't be picked, and a typed date outside them makes the field invalid. Use isDateUnavailable for single days that can't be picked.
"use client"
import { CalendarDate } from "@internationalized/date"Controlled#
Pass value and onChange to own the range. startName and endName submit each end with a form as an ISO date.
from=2026-11-02 to=2026-11-08
"use client"
import * as React from "react"Invalid#
Set isInvalid and an errorMessage to explain what to change. Built-in validation, such as a date past maxValue, shows the same way.
"use client"
import { CalendarDate } from "@internationalized/date"Disabled#
Use isDisabled for a period the user can see but not change. Use isReadOnly to keep it focusable and readable.
"use client"
import { CalendarDate } from "@internationalized/date"Server rendering#
With no value, defaultValue, or placeholderValue, the empty segments are built from the current date, and without an I18nProvider the locale comes from the browser. Pass a placeholderValue (or a value) and wrap the page in I18nProvider so the server and client render the same.
<I18nProvider locale="en-US">
<DateRangePicker
label="Statement period"
placeholderValue={new CalendarDate(2026, 11, 1)}
/>
</I18nProvider>Accessibility#
DateRangePicker is built on the React Aria DateRangePicker. It provides:
- Each date segment as a spin button: arrow keys change it, digits type into it, and focus moves to the next segment when one is full
- The label, description, and error message linked to both inputs
- The calendar button named and kept pressed while the calendar is open
- Focus moved into the calendar on open and back to the button on close
- Arrow key, Page Up and Page Down, and Home and End navigation in the calendar grid
- Preset chips as a single-choice group
Label the picker#
Give every picker a label. When the label isn't visible, such as the small pill in a filter row, pass an aria-label instead. Name the preset chips with presetsLabel.
<DateRangePicker aria-label="Filter by period" size="sm" />Explain errors in words#
The invalid state uses a destructive color. Always pass an errorMessage that says what to change, so the state is not conveyed by color alone.
API Reference#
DateRangePicker#
A complete range picker: label, field, help text, error, and the calendar overlay. Accepts all React Aria DateRangePicker props except children, className, and style, such as value, defaultValue, onChange, placeholderValue, minValue, maxValue, isDateUnavailable, allowsNonContiguousRanges, firstDayOfWeek, granularity, isDisabled, isReadOnly, isRequired, isInvalid, startName, and endName.
| Prop | Type | Default | Description |
|---|---|---|---|
label | React.ReactNode | - | Visible label. Without it, pass aria-label. |
description | React.ReactNode | - | Help text under the field. |
errorMessage | React.ReactNode | ((validation: ValidationResult) => ReactNode) | - | The reason the value is invalid. Shown only while it is. |
size | "default" | "sm" | "default" | "default" matches Input, "sm" is a pill. |
presets | DateRangePreset[] | - | Quick ranges above the calendar. |
presetsLabel | string | "Presets" | Names the preset chips for assistive tech. |
overlay | "auto" | "popover" | "sheet" | "auto" | Where the calendar opens. "auto" is a sheet on phones, a popover on wider screens. |
sheetTitle | React.ReactNode | - | Heading of the sheet. Defaults to the label, then aria-label, then "Choose dates". |
numberOfMonths | number | - | Months shown side by side in the popover. The sheet shows one. |
captionLayout | "label" | "dropdown" | - | "dropdown" puts month and year selects in the calendar header. |
className | string | - | Additional classes to apply to the root element. |
DateRangePreset#
A quick range shown as a chip above the calendar.
| Field | Type | Default | Description |
|---|---|---|---|
id | Key | - | The key reported when the chip is chosen. |
label | React.ReactNode | - | The chip text. |
value | DateRange | ((today: CalendarDate) => DateRange) | - | The range, or a function of today that returns it. Runs only on press. |
DateRangePickerRoot#
The styled React Aria DateRangePicker root for composing your own layout. Lays out the label, field, help text, and error, and lets the parts follow its disabled state.
| Prop | Type | Default | Description |
|---|---|---|---|
className | string | - | Additional classes to apply to the root element. |
...props | DateRangePickerProps<T> (React Aria) | - | Props spread to the React Aria root. |
DateRangePickerSeparator#
The dash between the two ends of the range. Hidden from assistive tech.
| Prop | Type | Default | Description |
|---|---|---|---|
children | React.ReactNode | "–" | The separator text. |
...props | React.ComponentProps<"span"> | - | Props spread to the <span>. |
See the React Aria documentation for more information.