shadcn.io is not affiliated with official shadcn/ui
Free Slot Scheduler Command Menu Block for React Aria
A booking tool's command menu docked at the end of a drawn week: chips for 15, 30 or 60 minutes and Once or Weekly recompute the free slots against busy blocks, a pane shows the week with the slot dashed, and Enter books it onto the week.
Installation
Overview
Find a time that fits with this scheduler command menu for React with React Aria. It opens from a week of availability in Slotharbour, a booking tool, whose header holds an outline field with its ⌘ K cap, and the week, Mon 19 to Fri 23 Oct 2026 from 09:00 to 17:00, is drawn under a soft scrim inside a hairline frame with a plus mark on each corner. The menu is the catalog Command docked at the end of the stage, with two ToggleGroup chip rows above the field, 15, 30 or 60 minutes and Once or Weekly. The list is the free slots, one per gap that the length fits in, found against typed busy blocks and against what has already been booked, so choosing 60 minutes makes the shorter gaps disappear and the foot says how many slots are left. The pane beside the list is the week with the busy blocks and the highlighted slot dashed. Enter books the slot, a block lands on the drawn week, and Weekly adds the note that it repeats every Tuesday, say. On a phone the pane sits under the list and the week shows three days. For preview the menu is drawn open in place when the page loads; the field, or Ctrl or ⌘ K while focus is in the block, opens the real command dialog with the same length, repeat and bookings. For booking pages, interview schedulers and any tool that has to offer a time.
FAQ
Why is the menu already open, and what do I keep?
So you can see it without a click. On load the menu is the Command drawn inline over a picture of a week of availability, in a plain shell that is not modal, so nothing is trapped, hidden or focused and the page around the preview keeps working. The header's field opens the real command dialog, modal, with its focus trap, Escape and focus return, and the inline copy stays closed until a reload. Both show the same Palette and share the block's state, so a chip, a booking or a search in the copy is in the dialog. In your app keep the dialog and drop the copy: the "in-place" state, the scrim with its shell and the Escape and blur effects.
How are the free slots found?
For each day spansOn lists the busy stretches (the typed blocks of the host's calendar plus the blocks the menu booked) in order, and gapsOf returns the gaps between them inside 09:00 to 17:00. A gap becomes a row only when it is at least as long as the chosen length, and the row offers the gap's start. Because the menu's own bookings count as busy, a slot you book disappears and the gap around it shrinks.
How do the chips behave?
Each group always keeps one chip chosen, so pressing the chosen one does nothing, and choosing another starts a new list and says "Looking for 60 minute slots." on the status line. The chips sit above the field, outside the command, so Enter and the arrow keys in the field never reach them, and they wrap to two lines on a phone without changing the height of the band.
What does Weekly do?
It adds a note, nothing more: the row's second line reads "repeats every Tuesday" and the status line says the booking repeats and that later weeks are not checked here. The demo has one week of data, so it cannot know whether next Tuesday is free. In your app check the following weeks in rowsFor before offering a repeating slot, or offer only the weeks where it fits.
What stays local, and how do I wire it?
All of it. The eleven busy blocks and the working hours are typed constants, and booking a slot only adds a block to state. In your app load BUSY from your calendar for the visible week, create the event through your API in pick and add it to the busy list only when the call succeeds, and give DAYS and the working hours the host's time zone.
Is it accessible from the keyboard?
Yes. Type to search by weekday or time, the arrow keys move through the slots and the pane follows, Enter books the slot, and Tab reaches the length and repeat chips above the field. Escape closes the menu with focus on the field, and Ctrl or ⌘ K opens the dialog while focus is inside the block, and only then. The rows and the empty state are menuitems of a React Aria menu inside an autocomplete; nothing is highlighted until you press an arrow key. React Aria has no highlight event, so the block reads the input's aria-activedescendant and the hovered row.