DateRangePicker
Unstable: a range of dates from a trigger in Select's look, through a popover with presets, one month of calendar and Cancel and Apply. Values are ISO plain dates; "today" is counted in the time zone you pass. Import it from @nizivo/react/unstable/date-range-picker.
Import
import { DateRangePicker } from '@nizivo/react/unstable/date-range-picker';
// Once per app (Getting started), after
// @nizivo/tokens/tokens.css and @nizivo/react/css/base.css and @nizivo/react/css/button.css and @nizivo/react/css/popover.css and @nizivo/react/css/select.css
import '@nizivo/react/css/date-range-picker.css';Unstable
It ships at @nizivo/react/unstable/date-range-picker, outside the semver promise, because the
design has not drawn an unavailable day or a comparison with the previous period yet. Its props may
change in a minor release.
Static or interactive
Client. It is React Aria's RangeCalendar inside the Popover. The server renders the trigger, with the applied range as its text, and the hidden form input; the popover, the calendar and anything that depends on today's date render only after hydration, when it opens. How to choose.
Value
value and defaultValue are { start, end }, ISO plain dates ('2026-10-01'), both ends
included, or null. Nothing is applied until Apply: choosing a preset or two days only changes what
the popover shows, and Cancel, Escape or a press outside keep the old range. onValueChange then
gets the range and { reason: 'preset' } or { reason: 'calendar' }. With name, the form gets one
field whose value is the ISO interval 2026-10-01/2026-10-07.
timeZone is required: the presets and the today mark count from today in it. min and max limit
the days that can be picked.
Presets
presets lists them in order: today, yesterday, last-7-days, last-30-days, this-quarter,
year-to-date (all six by default; [] for none). Each ends today. A chosen preset is shown pressed
until a day is picked.
Reading the range
The trigger and the footer read the range as labels.range, '{start} – {end}' by default, each
end formatted on its own in locale ("Oct 1 – Oct 7, 2026"; the start drops the year when both
share it), never with Intl.DateTimeFormat.formatRange, whose spacing differs between browsers. Inside
a Field, the trigger is named by its value and the label, as Select's is, and a press on the label
focuses it without opening the popover.
Examples
Basic
The report period: Figma's picker with "Oct 1 – Oct 7, 2026" applied. Open it, pick a preset or two days, then Apply; Cancel, Escape or a press outside keep the old range. The form gets period=2026-10-01/2026-10-07.
import type { JSX } from 'react';
import { Field } from '@nizivo/react/field';
import { DateRangePicker } from '@nizivo/react/unstable/date-range-picker';
export function DateRangePickerExample(): JSX.Element {
return (
<Field label="Report period">
<DateRangePicker
name="period"
timeZone="UTC"
defaultValue={{ start: '2026-10-01', end: '2026-10-07' }}
/>
</Field>
);
}
Empty
No range yet: the placeholder; no presets, only the month.
import type { JSX } from 'react';
import { Field } from '@nizivo/react/field';
import { DateRangePicker } from '@nizivo/react/unstable/date-range-picker';
export function DateRangePickerEmptyExample(): JSX.Element {
return (
<Field label="Delivery window">
<DateRangePicker timeZone="Europe/Berlin" presets={[]} />
</Field>
);
}
States
Sizes, an error from the server, and disabled.
import type { JSX } from 'react';
import { Field } from '@nizivo/react/field';
import { DateRangePicker } from '@nizivo/react/unstable/date-range-picker';
export function DateRangePickerStatesExample(): JSX.Element {
const range = { start: '2026-09-28', end: '2026-10-04' };
return (
<div style={{ display: 'grid', gap: 16, justifyItems: 'start' }}>
<DateRangePicker aria-label="Period" timeZone="UTC" size="sm" defaultValue={range} />
<DateRangePicker aria-label="Period" timeZone="UTC" size="lg" defaultValue={range} />
<Field label="Campaign dates" error="Pick dates after the campaign was created.">
<DateRangePicker timeZone="UTC" defaultValue={range} />
</Field>
<Field label="Archived period" disabled>
<DateRangePicker timeZone="UTC" defaultValue={range} />
</Field>
</div>
);
}
Props
Client component Renders no element of its own. Props: DateRangePickerProps. Source: src/unstable/date-range-picker/date-range-picker.client.tsx.
| Name | Type | Default | Description |
|---|---|---|---|
defaultOpen | boolean | – | Opens the popover on the first render (after hydration). |
defaultValue | DateRange | null | – | Uncontrolled starting range. |
invalid | boolean | – | |
labels | Partial<DateRangePickerLabels> | – | |
locale | string | – | BCP 47 locale for the dates. Defaults to NizivoConfig's, else |
max | string | – | Latest pickable date, ISO. |
min | string | – | Earliest pickable date, ISO. |
name | string | – | The form field: one hidden input whose value is the ISO interval |
onValueChange | (value: DateRange, details: ChangeDetails<DateRangePickerValueReason>) => void | – | Called when Apply keeps a new range. |
presets | readonly DateRangePreset[] | all six | The presets beside the calendar, in this order; |
size | ControlSize"sm""md""lg" | 'md' | 32 / 40 / 48 px, as Select's trigger. |
timeZonerequired | string | – | The time zone "today" is in, for the presets and the today mark (§16.3). |
value | DateRange | null | – | Controlled range; |
- Also takes every attribute of
<button>exceptchildren,defaultValue,onChange,type,value;nameis redeclared above. (Omit<ComponentPropsWithRef<"button">, "value" | "defaultValue" | "onChange" | "children" | "type">)
Other exports
DateRangetypeimport type { DateRange } from '@nizivo/react/unstable/date-range-picker'A range of ISO plain dates (
'2026-10-01'), both ends included (§16.3).Name Type Default Description endrequiredstring– startrequiredstring– DateRangePickerDayStatetypeimport type { DateRangePickerDayState } from '@nizivo/react/unstable/date-range-picker'A day cell's state, for its classes and data attributes.
Name Type Default Description daterequiredstring– The cell's ISO date.
disabledrequiredboolean– focusVisiblerequiredboolean– hoveredrequiredboolean– outsideMonthrequiredboolean– pressedrequiredboolean– rangeEndrequiredboolean– rangeStartrequiredboolean– selectedrequiredboolean– DateRangePickerLabelstypeimport type { DateRangePickerLabels } from '@nizivo/react/unstable/date-range-picker'DateRangePicker's own strings (§16.4): serialisable, English defaults.
Name Type Default Description applyrequiredstring'Apply'The footer's button that keeps the new one.
cancelrequiredstring'Cancel'The footer's button that keeps the old range.
last30Daysrequiredstring'Last 30 days'last7Daysrequiredstring'Last 7 days'placeholderrequiredstring'Select dates'The trigger's text with no range.
presetsrequiredstring'Presets'The presets list's name.
rangerequiredstring'{start} – {end}'How a range reads, in the trigger and the footer.
thisQuarterrequiredstring'This quarter'todayrequiredstring'Today'yearToDaterequiredstring'Year to date'yesterdayrequiredstring'Yesterday'DateRangePickerNavStatetypeimport type { DateRangePickerNavState } from '@nizivo/react/unstable/date-range-picker'Name Type Default Description disabledrequiredboolean– focusVisiblerequiredboolean– hoveredrequiredboolean– pressedrequiredboolean– DateRangePickerPropstypeimport type { DateRangePickerProps } from '@nizivo/react/unstable/date-range-picker'The props of
DateRangePicker, listed above.DateRangePickerStatetypeimport type { DateRangePickerState } from '@nizivo/react/unstable/date-range-picker'Name Type Default Description disabledrequiredboolean– filledrequiredboolean– A range is applied.
invalidrequiredboolean– openrequiredboolean– sizerequiredControlSize"sm""md""lg"– DateRangePickerValueReasontypeimport type { DateRangePickerValueReason } from '@nizivo/react/unstable/date-range-picker''preset','calendar': how the applied range was chosen;'clear'is not used yet.type DateRangePickerValueReason = "preset" | "calendar"DateRangePresettypeimport type { DateRangePreset } from '@nizivo/react/unstable/date-range-picker'A preset's key.
todayand the others are counted intimeZone.type DateRangePreset = "today" | "yesterday" | "last-7-days" | "last-30-days" | "this-quarter" | "year-to-date"
Data attributes and slots
Style parts by class (.nz-<slot>) and state by data attribute. Attribute meanings come from contracts/vocabulary.json.
| Slot | Rendered by | Data attributes |
|---|---|---|
date-range-picker-day.nz-date-range-picker-day | DateRangePickerinner element (CSS only); interactive, focus: self |
|
date-range-picker-nav.nz-date-range-picker-nav | DateRangePickerinner element (CSS only); interactive, focus: self |
|
date-range-picker-preset.nz-date-range-picker-preset | DateRangePickerinner element (CSS only); interactive, focus: self | none |
date-range-picker-trigger.nz-date-range-picker-trigger | DateRangePickerinner element (CSS only); interactive, focus: self | none |
CSS hooks
Set a hook on the component's class or any ancestor to restyle it without specificity games; leave it unset for the default shown.
| Hook | Default | Read as |
|---|---|---|
--nz-date-range-picker-chosen-bg | --nz-bg-brand-subtle |
|
--nz-date-range-picker-range-bg | --nz-bg-brand-subtle |
|
Layers: nz.components. Classes: .nz-date-range-picker-body, .nz-date-range-picker-calendar, .nz-date-range-picker-day, .nz-date-range-picker-footer, .nz-date-range-picker-grid, .nz-date-range-picker-header, .nz-date-range-picker-heading, .nz-date-range-picker-nav, .nz-date-range-picker-nav-icon, .nz-date-range-picker-popup, .nz-date-range-picker-preset, .nz-date-range-picker-presets, .nz-date-range-picker-range, .nz-date-range-picker-weekday, .nz-popover-content, .nz-popover-popup.
Tokens this stylesheet reads (29)
--nz-bg-brand, --nz-bg-brand-subtle, --nz-bg-hover, --nz-border-subtle, --nz-border-width-default, --nz-font-body-m, --nz-font-body-m-strong, --nz-font-label-m, --nz-font-label-m-strong, --nz-icon-secondary, --nz-radius-full, --nz-radius-sm, --nz-radius-xl, --nz-size-icon-md, --nz-space-0-5, --nz-space-1, --nz-space-2, --nz-space-4, --nz-space-5, --nz-space-8, --nz-text-brand, --nz-text-disabled, --nz-text-on-brand, --nz-text-primary, --nz-text-tertiary, --nz-tracking-body-m, --nz-tracking-body-m-strong, --nz-tracking-label-m, --nz-tracking-label-m-strong
Accessibility
These parts are interactive. Each gets the focus outline and touch-action rule from @nizivo/react/css/base.css (the nz.base layer), so the floor survives a restyle:
date-range-picker-day: draws the focus outline itself on keyboard focus.date-range-picker-nav: draws the focus outline itself on keyboard focus.date-range-picker-preset: draws the focus outline itself on keyboard focus.date-range-picker-trigger: draws the focus outline itself on keyboard focus.
Keyboard
| Keys | Action |
|---|---|
| Enter, Space | On the trigger, opens the popover; focus moves into it. |
| Tab | Moves between the presets, the month buttons, the calendar and Cancel and Apply. |
| Arrow keys | In the calendar, move a day or a week. |
| Page Up, Page Down | In the calendar, move a month. |
| Home, End | In the calendar, move to the start or end of the week. |
| Enter | In the calendar, picks the range's first day and moves on a day; then picks its last day. |
| Escape | Closes the popover and keeps the old range; focus returns to the trigger. |
Source: React Aria Components 1.22.1 RangeCalendar (_engines/react-aria/date-range-picker.client.tsx) inside the Popover. The browser lane (test/browser/date-range-picker.spec.tsx) checks hydration, the trigger's name, opening on the applied month, presets, Apply, Cancel, Escape, two days by keyboard, the time zone's today and the form's value.
Figma
Node ids from design/component-map.json, which nz gen writes from component.json. The links open the Nizivo Figma library, which may ask you to sign in.
- Canonical: Figma node
746:267 - Ghost masters: none
DataView
The same records as a table on a wide screen and as a list on a narrow one. The server renders both and a container query shows one, by the view's own width, so nothing changes on hydration.
Delta
A signed change, such as +12.4%, coloured by direction and read with a visually hidden comparison. The sign carries the direction without colour.