Skip to content
Sign in
Apps SDKAvailability pickerMenu

Availability pickerPro

Open times from someone's calendar, picked and booked without leaving the thread, in your time zone or theirs.

ProThirty minutes with a type designer in New York, booked from Lisbon.

Installation

$ pnpm dlx shadcn@latest add @chatcn-pro/availability-picker

Usage

tsx
import { AvailabilityPicker } from "@/components/ui/chatcn/pro/availability-picker"

<AvailabilityPicker
  title="Licensing call with Odile Marchetti"
  duration={30}
  slots={slots}
  timeZone="Europe/Lisbon"
  otherTimeZone="America/New_York"
  otherName="Odile"
  value={start}
  onValueChange={setStart}
  status={status}
  onBook={({ start, end }) => book(start, end)}
/>

Slots are start times with an offset; every slot lasts duration minutes. The week strip groups them by calendar day in the zone being shown, and three small marks under each date say how open the day is, so the fullest day is found before it is opened. Pass days to keep fully booked days visible, struck through.

With otherTimeZone, a switch flips every time between your zone and theirs, and the readout under the grid always gives the pick in the other zone too. Times outside workingHours in your own zone carry a small sunrise or moon mark, so a 21:00 call is never booked by accident. The book button names the exact day and time.

Move status to booking while the calendar confirms, then to booked, which folds the picker into one line with the time in both zones. A slot taken while someone looked goes in bookError with what to do next. Render it inside the card your host draws for a widget answer. It fills the width it is given, so a comfortable max width (around 28 rem) comes from the card, and it follows the host theme through the kit tokens. Pass onExpand where the host can go full screen.

Examples

States

ProBooked, a slot taken while you looked, booking, loading, a long day, no times, an error and disabled.

Props

PropTypeNotes
title, metaReactNodeWhat is booked, and a quiet line after the duration
durationnumberMinutes per slot
slots{ start: string | number | Date }[]Open times, any order
daysstring[]YYYY-MM-DD days to show even when full
timeZone, otherTimeZone, otherNamestringIANA zones and a short name
value, defaultValue, onValueChangestring | nullThe picked slot's start
status"ready" | "loading" | "error" | "booking" | "booked"Default ready
onBook({ start, end }) => voidThe primary action
bookError, bookedNote, onChangeTimeFailed booking, booked line, pick again
onPrevious, onNext, rangeLabelWeek paging
visibleSlotsnumberTimes before the fold. Default 9
workingHours[number, number]Your normal hours. Default 8 to 20
locale, hour12string, booleanDate and time formatting. Default en-GB
onRetry, onExpand, disabled
labelsPartial<…Labels>Every string the widget writes, for translation

Labels

labels covers the zone switch, week arrows, day and time group names, the fold, the early and late marks, the readout, every button state and the empty and error copy. Strings with values are functions: book(when), booked(when), range(from, to), inOther(time, name). Dates and times come from Intl in locale.

Keyboard

KeyAction
TabWeek arrows, zone switch, day strip, times, then Book
Arrow keysMove between days, or between times, and pick
Enter or SpaceBook on the book button

Accessibility

  • Days and times are native radio groups. Each day is named with its full date and how many times it has, and full days are disabled.
  • Early and late are said in the time's name, not only drawn as an icon.
  • The readout is a polite live region, so the pick and its time in the other zone are announced. Booked is a status message.
  • Touch targets grow to 44 px on coarse pointers.

Motion

Times press to 0.97 and change fill in 150 ms. The booked line enters with a 220 ms fade from 0.96. Keyboard moves never animate, and reduced motion keeps only the fade.