RSVP cardPro
An invite answered in the thread, drawn against your own calendar so a clash is seen before it is read, with a counter proposal from times you are free.
On this page
Installation
Usage
import { RsvpCard } from "@/components/ui/chatcn/pro/rsvp-card"
<RsvpCard
title="Specimen review, Corvo Serif"
start="2026-11-12T14:00:00Z"
end="2026-11-12T15:00:00Z"
organizer="Ilse Brandt"
calendar={myThursday}
timeZone="Europe/Lisbon"
onReply={(reply) => sendReply(reply)}
onPropose={(slot, note) => propose(slot, note)}
/>Pass the viewer's other events that day as calendar. The card draws a few hours of that day as a ruler: your events on the top lane, the invite on the bottom one. An overlap lights an amber column through both lanes and hatches the part of the invite that clashes, and the line under the ruler names the clash in words. With nothing in the way it says so, in green.
Going, Maybe and Can't go are optimistic: the choice shows at once, and if onReply returns a promise that rejects, the reply rolls back and the card says how to fix it. Propose time opens free slots of the same length on the same day, nearest first, computed from calendar and workingHours unless you pass suggestions. The picked one is outlined on the ruler before it is sent.
Free to the free event-card, this adds the reply, the conflict check and the counter proposal. 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
Props
| Prop | Type | Notes |
|---|---|---|
title, start, end, location | The event | |
organizer | string | Who sent it |
guests | { going?, maybe?, declined?, waiting? } | Counts for the guest line |
calendar | { title, start, end }[] | Your other events that day |
suggestions | { start, end }[] | Times to propose. Defaults to computed free slots |
workingHours | [number, number] | Hours a computed slot may use. Default 9 to 18 |
reply, defaultReply, onReply | "going" | "maybe" | "declined" | Optimistic |
onPropose, proposal, onWithdraw | Counter proposal, sent or not | |
status | "ready" | "loading" | "error" | "cancelled" | Default ready |
timeZone, locale, hour12 | Formatting. Default en-GB | |
onRetry, disabled | ||
labels | Partial<…Labels> | Every string the widget writes, for translation |
Labels
labels covers the three replies and their announcements, the propose panel, the clash and free lines, the ruler names, the organiser and guest lines, the cancelled copy and both error messages. clash(title, time), proposed(time, to), guests(counts) and range(from, to) are functions.
Keyboard
| Key | Action |
|---|---|
| Tab | Reply buttons, then Propose time |
| Enter or Space | Reply, or open the propose panel |
| Arrow keys | Move between suggested times in the panel |
Accessibility
- Replies are toggle buttons with
aria-pressedin a labelled group; the chosen reply is announced politely once it has been saved. - The ruler is a labelled figure, and every event on it keeps its name for screen readers even when it is too narrow to print it. The clash is also written out under the ruler, with an icon, not only drawn in amber.
- Suggested times are a native radio group. Opening the panel moves focus to the first time.
Motion
The propose panel and the sent proposal enter with a 180 to 220 ms fade from 0.96. Reply buttons press to 0.97. Reduced motion keeps the fades only.