Marble UI
← Components

Time Slot Picker

Preview: Default

Grid of bookable time slots grouped by part of day (radio group: arrow keys move, booked slots disabled).

Import

import { TimeSlotPicker } from '@marbleui/ui'

Props

<TimeSlotPicker>

PropTypeDefaultDescription
groups *required{ title: string; slots: TimeSlot[]; }[]—
onChange *required(id: string) => void—
classNamestring—
labelstring'Time'
valuestring—

Accessibility

Keyboard interaction, focus management and ARIA semantics come from the underlying primitives: React Aria Radio · React Aria RadioGroup. Follow the primitive's docs for the full keyboard map.

Usage notes

TimeSlotPicker shows bookable times as a grid grouped by part of day, with booked slots crossed out and disabled. It is a radio group, so one slot is chosen at a time; for free time entry use TimeField.

const [slot, setSlot] = useState<string>()
<TimeSlotPicker
  label="Meeting time, Tuesday 21 October"
  groups={[
    { title: 'Morning', slots: [{ id: '09:00', label: '9:00 AM' }, { id: '09:30', label: '9:30 AM', disabled: true }] },
    { title: 'Afternoon', slots: [{ id: '13:00', label: '1:00 PM' }] },
  ]}
  value={slot}
  onChange={setSlot}
/>
  • Controlled: onChange receives the slot id; store it and pass it back as value. Without value nothing shows as selected, and there is no defaultValue.
  • label (default “Time”) is only the accessible name and isn’t rendered, so include the date in it. Each group’s title is both the visible heading and the group’s name.
  • Arrow keys move through all slots, across groups, skipping disabled ones, and select as they go. Book from a separate confirm button, not from onChange.
Example source · show
import { useState } from 'react'
import { TimeSlotPicker } from './time-slot-picker'

const mk = (hs: string[], booked: string[]) => hs.map((h) => ({ id: h, label: h, disabled: booked.includes(h) }))
const groups = [{ title: 'Morning', slots: mk(['09:00', '09:30', '10:00', '10:30', '11:00', '11:30'], ['09:30', '11:00']) }, { title: 'Afternoon', slots: mk(['13:00', '13:30', '14:00', '15:00', '15:30', '16:00'], ['14:00']) }]
function Demo() {
  const [v, setV] = useState<string>()
  return <div className="flex w-96 flex-col gap-3"><TimeSlotPicker label="Meeting time, Tuesday Oct 21" groups={groups} value={v} onChange={setV} /><span aria-live="polite" className="text-sm text-fg-muted">{v ? `Booked ${v}, 30 minutes` : 'Pick a slot'}</span></div>
}
export const styles = { Default: Demo }

Source: packages/ui/src/time-slot-picker/time-slot-picker.tsx

Catalog

Category
date-time
Priority
Extra
Primitive
composed
React Aria
GridList