Skip to main content

TimeRangePicker

A popover-based time range selector supporting 12/24-hour clocks, minute stepping, controlled/uncontrolled mode, scrollable hour/minute/AM-PM columns, and start/end part switching. Ideal for booking forms, scheduling interfaces, and time window selection in dashboards.


Usage

Basic usage

import { TimeRangePicker } from '@nofinite/nui';

<TimeRangePicker />;

Controlled usage

const [range, setRange] = useState({ from: '09:00', to: '17:00' });

<TimeRangePicker value={range} onChange={setRange} />;

24-hour clock

<TimeRangePicker clockType={24} />

Minute stepping

<TimeRangePicker minuteStep={15} />

Form integration

<TimeRangePicker nameFrom="startTime" nameTo="endTime" />

Disabled state

<TimeRangePicker disabled />

Props

PropTypeDefaultDescription
valueTimeRange Controlled time range {from: HH:mm, to: HH:mm}
defaultValueTimeRange Initial uncontrolled value
onChange(v:TimeRange) => void Change callback
clockType12 | 24 12Clock format
minuteStepnumber 1Minute increment
placeholderstring Select time rangeTrigger placeholder
nameFromstring Hidden input name for start time
nameTostring Hidden input name for end time
idstring Trigger id
classNamestring Styling override
disabledboolean falseDisabled state

Variants

Clock variants

<TimeRangePicker clockType={12} />
<TimeRangePicker clockType={24} />

Minute precision variants

<TimeRangePicker minuteStep={5} />
<TimeRangePicker minuteStep={30} />

Control mode variants

<TimeRangePicker defaultValue={{ from: "09:00", to: "17:00" }} />
<TimeRangePicker value={{ from: "10:30", to: "18:00" }} />

Available variants

  • 12-hour range picker
  • 24-hour range picker
  • Stepped minute range picker
  • Full precision minute range picker
  • Controlled/uncontrolled range picker
  • Disabled range picker
  • Form-integrated range picker
  • Start/End toggle UI

Guidelines

  • Default minuteStep ≥ 5 for faster selection
  • Use 24-hour clock for international or enterprise apps
  • Use 12-hour clock for consumer-friendly booking UX
  • Always show placeholder until user selects both times
  • Ensure popover width fits both from → to labels

States

  • Closed trigger
  • Open popover
  • Active part (Start/End)
  • Selected hour/minute/AM-PM
  • Hover item
  • Disabled
  • Placeholder
  • Focus-visible trigger

Keyboard Interaction

  • Trigger opens popover via Enter / Space
  • Tab navigates start/end buttons and scroll columns
  • Arrow navigation scrolls hour/minute/AM-PM items
  • Escape closes popover
  • Focus restores to trigger on close
  • Footer buttons are accessible via Tab/Enter

Accessibility

  • Trigger uses aria-haspopup="dialog"
  • aria-expanded reflects popover state
  • Hidden inputs enable form submission
  • Focus restoration ensures keyboard continuity
  • Selected items are visually highlighted
  • Start/End part toggle is keyboard accessible
  • Placeholder indicates empty range

Design Tokens

TokenUsage
--nui-bg-surfacePanel background
--nui-bg-mutedDisabled trigger
--nui-bg-subtleHover item, footer button background
--nui-fg-defaultText
--nui-fg-subtlePlaceholder, inactive part toggle
--nui-fg-disabledDisabled text
--nui-border-defaultColumn borders, panel borders
--nui-border-hoverTrigger hover border
--nui-color-primarySelected item
--nui-color-primary-fgSelected text
--nui-brand-100Focus ring
--nui-radius-md/lgBorder radius
--nui-space-*Padding & gap
--nui-z-dropdownPopover layering

Best Practices

Do

  • Use stepped minutes for faster selection
  • Switch activePart for proper Start/End selection
  • Maintain focus restoration
  • Use hidden inputs for form integration
  • Use 24h clock in global apps
  • Combine with date picker for scheduling workflows

Don’t

  • Allow very small minuteStep values that break UX
  • Disable collision positioning
  • Use extremely narrow triggers with long from → to labels
  • Hide popover without focus restoration
  • Mix uncontrolled and controlled values inconsistently