Skip to content
Nizivo UI

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.

Tier: ownUnstable

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.

Pick dates after the campaign was created.
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.

NameTypeDefaultDescription
defaultOpenboolean–

Opens the popover on the first render (after hydration).

defaultValueDateRange | null–

Uncontrolled starting range.

invalidboolean–
labelsPartial<DateRangePickerLabels>–
localestring–

BCP 47 locale for the dates. Defaults to NizivoConfig's, else 'en-US'.

maxstring–

Latest pickable date, ISO.

minstring–

Earliest pickable date, ISO.

namestring–

The form field: one hidden input whose value is the ISO interval start/end.

onValueChange(value: DateRange, details: ChangeDetails<DateRangePickerValueReason>) => void–

Called when Apply keeps a new range.

presetsreadonly DateRangePreset[]all six

The presets beside the calendar, in this order; [] shows none.

sizeControlSize
"sm""md""lg"
'md'

32 / 40 / 48 px, as Select's trigger.

timeZonerequiredstring–

The time zone "today" is in, for the presets and the today mark (§16.3).

valueDateRange | null–

Controlled range; null is no range. Pair it with onValueChange.

  • Also takes every attribute of <button> except children, defaultValue, onChange, type, value; name is redeclared above. (Omit<ComponentPropsWithRef<"button">, "value" | "defaultValue" | "onChange" | "children" | "type">)

Other exports

DateRange type

import type { DateRange } from '@nizivo/react/unstable/date-range-picker'

A range of ISO plain dates ('2026-10-01'), both ends included (§16.3).

NameTypeDefaultDescription
endrequiredstring–
startrequiredstring–
DateRangePickerDayState type

import type { DateRangePickerDayState } from '@nizivo/react/unstable/date-range-picker'

A day cell's state, for its classes and data attributes.

NameTypeDefaultDescription
daterequiredstring–

The cell's ISO date.

disabledrequiredboolean–
focusVisiblerequiredboolean–
hoveredrequiredboolean–
outsideMonthrequiredboolean–
pressedrequiredboolean–
rangeEndrequiredboolean–
rangeStartrequiredboolean–
selectedrequiredboolean–
DateRangePickerLabels type

import type { DateRangePickerLabels } from '@nizivo/react/unstable/date-range-picker'

DateRangePicker's own strings (§16.4): serialisable, English defaults.

NameTypeDefaultDescription
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'
DateRangePickerNavState type

import type { DateRangePickerNavState } from '@nizivo/react/unstable/date-range-picker'

NameTypeDefaultDescription
disabledrequiredboolean–
focusVisiblerequiredboolean–
hoveredrequiredboolean–
pressedrequiredboolean–
DateRangePickerProps type

import type { DateRangePickerProps } from '@nizivo/react/unstable/date-range-picker'

The props of DateRangePicker, listed above.

DateRangePickerState type

import type { DateRangePickerState } from '@nizivo/react/unstable/date-range-picker'

NameTypeDefaultDescription
disabledrequiredboolean–
filledrequiredboolean–

A range is applied.

invalidrequiredboolean–
openrequiredboolean–
sizerequiredControlSize
"sm""md""lg"
–
DateRangePickerValueReason type

import 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"

DateRangePreset type

import type { DateRangePreset } from '@nizivo/react/unstable/date-range-picker'

A preset's key. today and the others are counted in timeZone.

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.

SlotRendered byData attributes
date-range-picker-day
.nz-date-range-picker-day
DateRangePicker
inner element (CSS only); interactive, focus: self
  • data-disabled (present or absent) ← disabled
    Mirrors the disabled attribute
  • data-focus-inset (present or absent)
    a11y-floor opt-out: draw the focus outline inside the part (outline-offset -2px), for clipped scrollers such as DialogBody and table cells (§8.5)
  • data-focus-visible (present or absent)
    Focus is visible; elsewhere use :focus-visible
  • data-outside-month (present or absent)
    Date outside the visible month
  • data-pressed (present or absent)
    Pointer or keyboard press (native parts use :active)
  • data-range-end (present or absent)
    Last date of the selected range
  • data-range-start (present or absent)
    First date of the selected range
  • data-selected (present or absent)
    Selected
  • data-today (present or absent)
    Today's date
date-range-picker-nav
.nz-date-range-picker-nav
DateRangePicker
inner element (CSS only); interactive, focus: self
  • data-disabled (present or absent) ← disabled
    Mirrors the disabled attribute
  • data-focus-visible (present or absent)
    Focus is visible; elsewhere use :focus-visible
  • data-pressed (present or absent)
    Pointer or keyboard press (native parts use :active)
date-range-picker-preset
.nz-date-range-picker-preset
DateRangePicker
inner element (CSS only); interactive, focus: self
none
date-range-picker-trigger
.nz-date-range-picker-trigger
DateRangePicker
inner 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.

HookDefaultRead as
--nz-date-range-picker-chosen-bg--nz-bg-brand-subtle
  • background on .nz-date-range-picker-preset[aria-pressed="true"]
--nz-date-range-picker-range-bg--nz-bg-brand-subtle
  • background on .nz-date-range-picker-day[data-selected]

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

KeysAction
Enter, SpaceOn the trigger, opens the popover; focus moves into it.
TabMoves between the presets, the month buttons, the calendar and Cancel and Apply.
Arrow keysIn the calendar, move a day or a week.
Page Up, Page DownIn the calendar, move a month.
Home, EndIn the calendar, move to the start or end of the week.
EnterIn the calendar, picks the range's first day and moves on a day; then picks its last day.
EscapeCloses 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.

On this page