Range Picker

A date/time range picker built on the Temporal API. Supports date-only and date+time modes, timezone awareness, keyboard input, and flexible popup placement.

Installation

npm install @dayflow/ui-range-picker
pnpm add @dayflow/ui-range-picker
yarn add @dayflow/ui-range-picker
bun add @dayflow/ui-range-picker

For projects that already use Tailwind CSS, import the component-only bundle:

@import '@dayflow/ui-range-picker/dist/styles.components.css';
@import 'tailwindcss';

For projects that do not use Tailwind CSS, import the full stylesheet instead:

import { RangePicker } from '@dayflow/ui-range-picker';
import '@dayflow/ui-range-picker/dist/styles.css';

Basic Usage

import { useState } from 'react';
import { Temporal } from 'temporal-polyfill';
import { RangePicker } from '@dayflow/ui-range-picker';
import type { ZonedRange } from '@dayflow/ui-range-picker';

function MyComponent() {
  const [range, setRange] = useState<ZonedRange>([
    Temporal.Now.zonedDateTimeISO(),
    Temporal.Now.zonedDateTimeISO().add({ hours: 1 }),
  ]);

  return <RangePicker value={range} onChange={value => setRange(value)} />;
}

Date-Only Mode

Pass showTime={false} to hide the time selector.

<RangePicker
  value={range}
  showTime={false}
  format='YYYY-MM-DD'
  onChange={value => setRange(value)}
/>

With Timezone

<RangePicker
  value={range}
  timeZone='America/New_York'
  onChange={(value, dateStrings) => {
    console.log('range:', value);
    console.log('formatted:', dateStrings); // ['2024-10-15 10:00', '2024-10-15 11:00']
  }}
/>

Custom Time Format

<RangePicker
  value={range}
  format='MM/DD/YYYY'
  showTime={{ format: 'hh:mm A' }}
  onChange={value => setRange(value)}
  onOk={value => saveToBackend(value)}
/>

The popup defaults to bottomLeft and automatically adjusts to avoid viewport overflow.

<RangePicker
  value={range}
  placement='topRight'
  autoAdjustOverflow={true}
  onChange={value => setRange(value)}
/>

Locale

Pass a BCP 47 locale string to localize month names and weekday labels.

<RangePicker value={range} locale='zh-CN' onChange={value => setRange(value)} />

Match Trigger Width

<RangePicker
  value={range}
  matchTriggerWidth
  onChange={value => setRange(value)}
/>

API Reference

RangePicker

PropTypeDefaultDescription
value[Temporal.PlainDate | PlainDateTime | ZonedDateTime, ...]—Controlled range value. Accepts any mix of Temporal types.
formatstring"YYYY-MM-DD"Display and parse format for date part
showTimeboolean | { format?: string }trueEnable time selection. Pass an object to set a custom time format.
showTimeFormatstring"HH:mm"Default time format when showTime is true
onChange(value: ZonedRange, dateString: [string, string]) => void—Fires on every selection change
onOk(value: ZonedRange, dateString: [string, string]) => void—Fires when the user confirms the selection with the OK button
timeZonestring—IANA timezone string (e.g. "America/New_York"). Defaults to system zone.
disabledbooleanfalseDisables all interactions
placement'bottomLeft' | 'bottomRight' | 'topLeft' | 'topRight''bottomLeft'Preferred popup position
autoAdjustOverflowbooleantrueFlip placement automatically when the popup would overflow the viewport
getPopupContainer() => HTMLElement—Mount the popup inside a custom container instead of document.body
matchTriggerWidthbooleanfalseSet the popup width to match the trigger input width
localestring | { code: string; messages?: Record<string, string> }'en-US'BCP 47 locale code for month/weekday labels

ZonedRange

type ZonedRange = [Temporal.ZonedDateTime, Temporal.ZonedDateTime];

The onChange and onOk callbacks always receive a ZonedRange regardless of the input value type, normalized to the active timezone.

On this page