shadcn.io is not affiliated with official shadcn/ui
Meeting Scheduler Calendar Block for React
Choose who is in a meeting and who is optional, see everyone's busy hours for one day, and get the best one-hour windows ranked in words before proposing one.
Installation
Overview
Find one hour that suits a team, with this meeting scheduler for React with Base UI. A hairline frame with a plus mark on each corner starts with six people as chips: each toggles in or out of the meeting, and an Optional button makes an included person optional. A table shows every person's typed busy hours on Thursday 15 October from 09:00 to 17:00 (UTC+1), with a hatched mark for busy and nothing for free, an "All free" row with a check, and how many are free in each hour. A person who is left out stays as a dashed row, so the table never changes height. Under it the four best one-hour windows are ranked and written out ("10:00: 4 of 4 free", "13:00: all required free, 1 optional busy"). Choosing one lights its column and enables the Shine Button, which proposes it in this demo: no invites are sent. With nobody included an Empty state takes the place of the list. The table scrolls sideways on a phone under sticky names. It's built from Empty, Shine Button and Guides with a hand-built table, and a CSS entrance only. For project teams, interview panels and anyone who has lost an afternoon to finding a time.
FAQ
How are the windows ranked?
An hour where every required person is free comes first, then the hour with the most optional people free, then the most required people free, and earlier hours win a tie. windowsOf does this from the busy strings in PEOPLE and the two flag strings for who is in and who is optional, and keeps the top four. Change WINDOW_COUNT to show more.
What does Optional change?
An optional person never makes an hour worse: the window is "all required free" as long as the required people are free, and the optional person is counted in the words ("1 optional busy"). Making someone optional can promote an hour that was previously held back by them.
How do I load real calendars?
Replace the busy strings (one character per hour from 09:00, x for busy) with busy blocks fetched from your calendar API, converted to the same hour grid. Everything else, the table, the counts and the ranking, reads from those strings. The block treats all times as UTC+1 and does not convert zones.
How is the table read by a screen reader?
It is a real table with a caption, row headers for the people and column headers for the hours. Each cell has hidden text saying "Busy" or "Free", the people's roles are added to their row header (", optional", ", not included"), and the polite status line announces each change to the group and the chosen hour. The best-hours list is a radio group, so the arrow keys move between windows.
What stays local, and how do I wire it up?
Everything. Propose only changes state in the page and says "Proposed Thu 15 Oct, 10:00, in this demo. No invites were sent." Nothing is saved or emailed. To make it real, post the chosen hour and the people from propose to your calendar API, which creates the event and sends the invites.