Schedule
Read-only schedule shell for day, week, month, and list views.
Examples
Section titled “Examples”October 2026
Sun
Mon
Tue
Wed
Thu
Fri
Sat
function ScheduleStory({ categories: storyCategories = categories, eventBorderStyle, events: storyEvents = events, height, highlightDate = defaultHighlightDate, plugins, timezoneID = localTimezoneID, view, viewDate: initialViewDate = defaultViewDate,}: { categories?: ReadonlyArray<ScheduleCategory>; eventBorderStyle?: ScheduleEventBorderStyle; events?: ScheduleEventSource; height?: ScheduleHeight; highlightDate?: Instant; plugins?: SchedulePlugin[]; timezoneID?: string; view: ScheduleView; viewDate?: Instant;}) { const [viewDate, setViewDate] = useState<Instant>(() => initialViewDate); const paginationPlugin = useSchedulePaginationPlugin({ onViewDateChange: setViewDate, }); return ( <Schedule categories={storyCategories} eventBorderStyle={eventBorderStyle} events={storyEvents} height={height} highlightDate={highlightDate} plugins={plugins ?? [paginationPlugin]} timezoneID={timezoneID} view={view} viewDate={viewDate} /> );}
<ScheduleStory view={createScheduleMonthlyView()} />October 2026
Sun11
Mon12
Tue13
Wed14
Thu15
Fri16
Sat17
function ScheduleStory({ categories: storyCategories = categories, eventBorderStyle, events: storyEvents = events, height, highlightDate = defaultHighlightDate, plugins, timezoneID = localTimezoneID, view, viewDate: initialViewDate = defaultViewDate,}: { categories?: ReadonlyArray<ScheduleCategory>; eventBorderStyle?: ScheduleEventBorderStyle; events?: ScheduleEventSource; height?: ScheduleHeight; highlightDate?: Instant; plugins?: SchedulePlugin[]; timezoneID?: string; view: ScheduleView; viewDate?: Instant;}) { const [viewDate, setViewDate] = useState<Instant>(() => initialViewDate); const paginationPlugin = useSchedulePaginationPlugin({ onViewDateChange: setViewDate, }); return ( <Schedule categories={storyCategories} eventBorderStyle={eventBorderStyle} events={storyEvents} height={height} highlightDate={highlightDate} plugins={plugins ?? [paginationPlugin]} timezoneID={timezoneID} view={view} viewDate={viewDate} /> );}
<ScheduleStory events={weekEvents} view={createScheduleWeeklyView({maxHour: 18, minHour: 8})}/>Borderless Events
Section titled “Borderless Events”October 2026
Sun11
Mon12
Tue13
Wed14
Thu15
Fri16
Sat17
function ScheduleStory({ categories: storyCategories = categories, eventBorderStyle, events: storyEvents = events, height, highlightDate = defaultHighlightDate, plugins, timezoneID = localTimezoneID, view, viewDate: initialViewDate = defaultViewDate,}: { categories?: ReadonlyArray<ScheduleCategory>; eventBorderStyle?: ScheduleEventBorderStyle; events?: ScheduleEventSource; height?: ScheduleHeight; highlightDate?: Instant; plugins?: SchedulePlugin[]; timezoneID?: string; view: ScheduleView; viewDate?: Instant;}) { const [viewDate, setViewDate] = useState<Instant>(() => initialViewDate); const paginationPlugin = useSchedulePaginationPlugin({ onViewDateChange: setViewDate, }); return ( <Schedule categories={storyCategories} eventBorderStyle={eventBorderStyle} events={storyEvents} height={height} highlightDate={highlightDate} plugins={plugins ?? [paginationPlugin]} timezoneID={timezoneID} view={view} viewDate={viewDate} /> );}
<ScheduleStory eventBorderStyle="none" events={weekEvents} view={createScheduleWeeklyView({maxHour: 18, minHour: 8})}/>Left Border Events
Section titled “Left Border Events”October 2026
Sun11
Mon12
Tue13
Wed14
Thu15
Fri16
Sat17
function ScheduleStory({ categories: storyCategories = categories, eventBorderStyle, events: storyEvents = events, height, highlightDate = defaultHighlightDate, plugins, timezoneID = localTimezoneID, view, viewDate: initialViewDate = defaultViewDate,}: { categories?: ReadonlyArray<ScheduleCategory>; eventBorderStyle?: ScheduleEventBorderStyle; events?: ScheduleEventSource; height?: ScheduleHeight; highlightDate?: Instant; plugins?: SchedulePlugin[]; timezoneID?: string; view: ScheduleView; viewDate?: Instant;}) { const [viewDate, setViewDate] = useState<Instant>(() => initialViewDate); const paginationPlugin = useSchedulePaginationPlugin({ onViewDateChange: setViewDate, }); return ( <Schedule categories={storyCategories} eventBorderStyle={eventBorderStyle} events={storyEvents} height={height} highlightDate={highlightDate} plugins={plugins ?? [paginationPlugin]} timezoneID={timezoneID} view={view} viewDate={viewDate} /> );}
<ScheduleStory eventBorderStyle="left" events={weekEvents} view={createScheduleWeeklyView({maxHour: 18, minHour: 8})}/>Narrow Week
Section titled “Narrow Week”October 2026
Sun11
Mon12
Tue13
Wed14
Thu15
Fri16
Sat17
function ScheduleStory({ categories: storyCategories = categories, eventBorderStyle, events: storyEvents = events, height, highlightDate = defaultHighlightDate, plugins, timezoneID = localTimezoneID, view, viewDate: initialViewDate = defaultViewDate,}: { categories?: ReadonlyArray<ScheduleCategory>; eventBorderStyle?: ScheduleEventBorderStyle; events?: ScheduleEventSource; height?: ScheduleHeight; highlightDate?: Instant; plugins?: SchedulePlugin[]; timezoneID?: string; view: ScheduleView; viewDate?: Instant;}) { const [viewDate, setViewDate] = useState<Instant>(() => initialViewDate); const paginationPlugin = useSchedulePaginationPlugin({ onViewDateChange: setViewDate, }); return ( <Schedule categories={storyCategories} eventBorderStyle={eventBorderStyle} events={storyEvents} height={height} highlightDate={highlightDate} plugins={plugins ?? [paginationPlugin]} timezoneID={timezoneID} view={view} viewDate={viewDate} /> );}
<div className={narrowWeekStoryContainer}> <ScheduleStory events={weekEvents} view={createScheduleWeeklyView({ dayMinWidth: 128, maxHour: 18, minHour: 8, })} /></div>Availability
Section titled “Availability”October 2026
Sun11
Mon12
Tue13
Wed14
Thu15
Fri16
Sat17
function ScheduleStory({ categories: storyCategories = categories, eventBorderStyle, events: storyEvents = events, height, highlightDate = defaultHighlightDate, plugins, timezoneID = localTimezoneID, view, viewDate: initialViewDate = defaultViewDate,}: { categories?: ReadonlyArray<ScheduleCategory>; eventBorderStyle?: ScheduleEventBorderStyle; events?: ScheduleEventSource; height?: ScheduleHeight; highlightDate?: Instant; plugins?: SchedulePlugin[]; timezoneID?: string; view: ScheduleView; viewDate?: Instant;}) { const [viewDate, setViewDate] = useState<Instant>(() => initialViewDate); const paginationPlugin = useSchedulePaginationPlugin({ onViewDateChange: setViewDate, }); return ( <Schedule categories={storyCategories} eventBorderStyle={eventBorderStyle} events={storyEvents} height={height} highlightDate={highlightDate} plugins={plugins ?? [paginationPlugin]} timezoneID={timezoneID} view={view} viewDate={viewDate} /> );}
() => { const availabilityPlugin = useScheduleAvailabilityPlugin({ getUnavailableRanges: getStoryUnavailableRanges, }); return ( <ScheduleStory events={weekEvents} plugins={[availabilityPlugin]} view={createScheduleWeeklyView({maxHour: 19, minHour: 7})} /> );}Pagination Hotkeys
Section titled “Pagination Hotkeys”Use the left and right arrow keys to move between weeks.
October 2026
Sun11
Mon12
Tue13
Wed14
Thu15
Fri16
Sat17
() => { const [viewDate, setViewDate] = useState<Instant>(() => defaultViewDate); const paginationPlugin = useSchedulePaginationPlugin({ hasHotkeys: true, onViewDateChange: setViewDate, });
return ( <div className={paginationHotkeysStoryContainer}> <Text as="p" color="secondary" type="supporting"> Use the left and right arrow keys to move between weeks. </Text> <Schedule categories={categories} events={weekEvents} highlightDate={defaultHighlightDate} plugins={[paginationPlugin]} timezoneID={localTimezoneID} view={createScheduleWeeklyView({maxHour: 18, minHour: 8})} viewDate={viewDate} /> </div> );}Resizable Events
Section titled “Resizable Events”October 2026
Sun11
Mon12
Tue13
Wed14
Thu15
Fri16
Sat17
function ResizableEventsStory(): React.JSX.Element { const [viewDate, setViewDate] = useState<Instant>(() => defaultViewDate); const [storyEvents, setStoryEvents] = useState(() => events); const paginationPlugin = useSchedulePaginationPlugin({ onViewDateChange: setViewDate, }); const resizePlugin = useScheduleEventResizePlugin({ onResize: ({end, event, start}) => { setStoryEvents(currentEvents => currentEvents.map(currentEvent => { if ( currentEvent.id !== event.id || typeof currentEvent.start !== 'number' ) { return currentEvent; }
return {...currentEvent, end, start}; }), ); }, snapMinutes: 5, });
return ( <Schedule categories={categories} events={storyEvents} highlightDate={defaultHighlightDate} plugins={[paginationPlugin, resizePlugin]} timezoneID={localTimezoneID} view={createScheduleWeeklyView({maxHour: 18, minHour: 8})} viewDate={viewDate} /> );}
<ResizableEventsStory />Movable Events
Section titled “Movable Events”October 2026
Sun11
Mon12
Tue13
Wed14
Thu15
Fri16
Sat17
function MovableEventsStory(): React.JSX.Element { const views = useMemo( (): {label: string; view: ScheduleView}[] => [ {label: 'Month', view: createScheduleMonthlyView()}, {label: 'Week', view: createScheduleWeeklyView({minHour: 6})}, {label: 'Day', view: createScheduleDayView({minHour: 6})}, ], [], ); const [view, setView] = useState<ScheduleView>(views[1].view); const [viewDate, setViewDate] = useState<Instant>(() => defaultViewDate); const [storyEvents, setStoryEvents] = useState(() => events); const paginationPlugin = useSchedulePaginationPlugin({ onViewDateChange: setViewDate, }); const viewSelectorPlugin = useScheduleViewSelectorPlugin(views, { onChangeView: setView, }); const getIsMovable = useCallback( (event: CalendarEvent) => event.id !== 'customer-escalation', [], ); const movePlugin = useScheduleEventMovePlugin({ getIsMovable, onMove: async ({end, event, start}) => { await new Promise(resolve => setTimeout(resolve, 600)); setStoryEvents(currentEvents => currentEvents.map(currentEvent => { if (currentEvent.id !== event.id) { return currentEvent; }
if ( typeof currentEvent.start === 'number' && typeof start === 'number' && typeof end === 'number' ) { return {...currentEvent, end, start}; }
if ( typeof currentEvent.start !== 'number' && typeof start !== 'number' && typeof end !== 'number' ) { return {...currentEvent, end, start}; }
return currentEvent; }), ); }, snapMinutes: 15, });
return ( <div className={fillHeightStoryContainer}> <Schedule categories={categories} events={storyEvents} height="fill" highlightDate={defaultHighlightDate} plugins={[paginationPlugin, viewSelectorPlugin, movePlugin]} timezoneID={localTimezoneID} view={view} viewDate={viewDate} /> </div> );}
<MovableEventsStory />Creatable Events
Section titled “Creatable Events”October 2026
Sun11
Mon12
Tue13
Wed14
Thu15
Fri16
Sat17
function CreatableEventsStory({ hasDetails, view,}: { hasDetails?: boolean; view: ScheduleView;}): React.JSX.Element { const [viewDate, setViewDate] = useState<Instant>(() => defaultViewDate); const [storyEvents, setStoryEvents] = useState<CalendarEvent[]>(() => events); const paginationPlugin = useSchedulePaginationPlugin({ onViewDateChange: setViewDate, }); const popoverPlugin = useScheduleEventPopoverPlugin(); const createPlugin = useScheduleEventCreatePlugin({ defaultDurationMinutes: 60, renderContent: ({close, draft}) => ( <CreateEventForm draft={draft} hasDetails={hasDetails} onCancel={close} onCreate={title => { const createdEvent: CalendarEvent = draft.isAllDay ? { category: 'Sync', end: Temporal.Instant.fromEpochMilliseconds(draft.end) .toZonedDateTimeISO(localTimezoneID) .toPlainDate() .subtract({days: 1}), id: `created-${draft.start}`, start: Temporal.Instant.fromEpochMilliseconds(draft.start) .toZonedDateTimeISO(localTimezoneID) .toPlainDate(), title, } : { category: 'Sync', end: draft.end, id: `created-${draft.start}`, start: draft.start, title, }; setStoryEvents(currentEvents => [...currentEvents, createdEvent]); close(); }} /> ), snapMinutes: 15, });
return ( <Schedule categories={categories} events={storyEvents} highlightDate={defaultHighlightDate} plugins={[paginationPlugin, createPlugin, popoverPlugin]} timezoneID={localTimezoneID} view={view} viewDate={viewDate} /> );}
function CreateEventForm({ draft, hasDetails = false, onCancel, onCreate,}: { draft: ScheduleEventDraft; /** * Adds location, attendee, and notes fields, making the popover taller than * the space above or below most drafted events. */ hasDetails?: boolean; onCancel: () => void; onCreate: (title: string) => void;}): React.JSX.Element { const [title, setTitle] = useState(''); const [location, setLocation] = useState(''); const [attendees, setAttendees] = useState(''); const [notes, setNotes] = useState(''); const timeLabel = draft.isAllDay ? `${Temporal.Instant.fromEpochMilliseconds(draft.start) .toZonedDateTimeISO(localTimezoneID) .toLocaleString(undefined, { day: 'numeric', month: 'short', weekday: 'short', })} · All day` : `${Temporal.Instant.fromEpochMilliseconds(draft.start) .toZonedDateTimeISO(localTimezoneID) .toLocaleString(undefined, { hour: 'numeric', minute: '2-digit', weekday: 'short', })} – ${Temporal.Instant.fromEpochMilliseconds(draft.end) .toZonedDateTimeISO(localTimezoneID) .toLocaleString(undefined, {hour: 'numeric', minute: '2-digit'})}`;
return ( <form className={css({w: 80})} onSubmit={submitEvent => { submitEvent.preventDefault(); onCreate(title.trim() === '' ? 'Untitled event' : title.trim()); }}> <Layout content={ <LayoutContent className={css({display: 'grid', gap: 3})}> <TextInput hasAutoFocus label="Title" onChange={setTitle} placeholder="Add a title" value={title} /> <Text color="secondary" type="supporting"> {timeLabel} </Text> {hasDetails ? ( <> <TextInput label="Location" onChange={setLocation} placeholder="Add a location" value={location} /> <TextInput label="Attendees" onChange={setAttendees} placeholder="Add attendees" value={attendees} /> <TextArea label="Notes" onChange={setNotes} placeholder="Add notes" rows={8} value={notes} /> </> ) : null} </LayoutContent> } footer={ <LayoutFooter primaryButton={ <Button label="Create" size="sm" type="submit" variant="primary" /> } secondaryButton={ <Button label="Cancel" onClick={onCancel} size="sm" variant="ghost" /> } /> } header={<LayoutHeader title="New event" />} height="auto" /> </form> );}
<CreatableEventsStory view={createScheduleWeeklyView({maxHour: 18, minHour: 8})}/>Creatable Events Tall Form
Section titled “Creatable Events Tall Form”October 2026
Sun11
Mon12
Tue13
Wed14
Thu15
Fri16
Sat17
function CreatableEventsStory({ hasDetails, view,}: { hasDetails?: boolean; view: ScheduleView;}): React.JSX.Element { const [viewDate, setViewDate] = useState<Instant>(() => defaultViewDate); const [storyEvents, setStoryEvents] = useState<CalendarEvent[]>(() => events); const paginationPlugin = useSchedulePaginationPlugin({ onViewDateChange: setViewDate, }); const popoverPlugin = useScheduleEventPopoverPlugin(); const createPlugin = useScheduleEventCreatePlugin({ defaultDurationMinutes: 60, renderContent: ({close, draft}) => ( <CreateEventForm draft={draft} hasDetails={hasDetails} onCancel={close} onCreate={title => { const createdEvent: CalendarEvent = draft.isAllDay ? { category: 'Sync', end: Temporal.Instant.fromEpochMilliseconds(draft.end) .toZonedDateTimeISO(localTimezoneID) .toPlainDate() .subtract({days: 1}), id: `created-${draft.start}`, start: Temporal.Instant.fromEpochMilliseconds(draft.start) .toZonedDateTimeISO(localTimezoneID) .toPlainDate(), title, } : { category: 'Sync', end: draft.end, id: `created-${draft.start}`, start: draft.start, title, }; setStoryEvents(currentEvents => [...currentEvents, createdEvent]); close(); }} /> ), snapMinutes: 15, });
return ( <Schedule categories={categories} events={storyEvents} highlightDate={defaultHighlightDate} plugins={[paginationPlugin, createPlugin, popoverPlugin]} timezoneID={localTimezoneID} view={view} viewDate={viewDate} /> );}
function CreateEventForm({ draft, hasDetails = false, onCancel, onCreate,}: { draft: ScheduleEventDraft; /** * Adds location, attendee, and notes fields, making the popover taller than * the space above or below most drafted events. */ hasDetails?: boolean; onCancel: () => void; onCreate: (title: string) => void;}): React.JSX.Element { const [title, setTitle] = useState(''); const [location, setLocation] = useState(''); const [attendees, setAttendees] = useState(''); const [notes, setNotes] = useState(''); const timeLabel = draft.isAllDay ? `${Temporal.Instant.fromEpochMilliseconds(draft.start) .toZonedDateTimeISO(localTimezoneID) .toLocaleString(undefined, { day: 'numeric', month: 'short', weekday: 'short', })} · All day` : `${Temporal.Instant.fromEpochMilliseconds(draft.start) .toZonedDateTimeISO(localTimezoneID) .toLocaleString(undefined, { hour: 'numeric', minute: '2-digit', weekday: 'short', })} – ${Temporal.Instant.fromEpochMilliseconds(draft.end) .toZonedDateTimeISO(localTimezoneID) .toLocaleString(undefined, {hour: 'numeric', minute: '2-digit'})}`;
return ( <form className={css({w: 80})} onSubmit={submitEvent => { submitEvent.preventDefault(); onCreate(title.trim() === '' ? 'Untitled event' : title.trim()); }}> <Layout content={ <LayoutContent className={css({display: 'grid', gap: 3})}> <TextInput hasAutoFocus label="Title" onChange={setTitle} placeholder="Add a title" value={title} /> <Text color="secondary" type="supporting"> {timeLabel} </Text> {hasDetails ? ( <> <TextInput label="Location" onChange={setLocation} placeholder="Add a location" value={location} /> <TextInput label="Attendees" onChange={setAttendees} placeholder="Add attendees" value={attendees} /> <TextArea label="Notes" onChange={setNotes} placeholder="Add notes" rows={8} value={notes} /> </> ) : null} </LayoutContent> } footer={ <LayoutFooter primaryButton={ <Button label="Create" size="sm" type="submit" variant="primary" /> } secondaryButton={ <Button label="Cancel" onClick={onCancel} size="sm" variant="ghost" /> } /> } header={<LayoutHeader title="New event" />} height="auto" /> </form> );}
<CreatableEventsStory hasDetails view={createScheduleWeeklyView({maxHour: 18, minHour: 8})}/>Creatable Month Events
Section titled “Creatable Month Events”October 2026
Sun
Mon
Tue
Wed
Thu
Fri
Sat
function CreatableEventsStory({ hasDetails, view,}: { hasDetails?: boolean; view: ScheduleView;}): React.JSX.Element { const [viewDate, setViewDate] = useState<Instant>(() => defaultViewDate); const [storyEvents, setStoryEvents] = useState<CalendarEvent[]>(() => events); const paginationPlugin = useSchedulePaginationPlugin({ onViewDateChange: setViewDate, }); const popoverPlugin = useScheduleEventPopoverPlugin(); const createPlugin = useScheduleEventCreatePlugin({ defaultDurationMinutes: 60, renderContent: ({close, draft}) => ( <CreateEventForm draft={draft} hasDetails={hasDetails} onCancel={close} onCreate={title => { const createdEvent: CalendarEvent = draft.isAllDay ? { category: 'Sync', end: Temporal.Instant.fromEpochMilliseconds(draft.end) .toZonedDateTimeISO(localTimezoneID) .toPlainDate() .subtract({days: 1}), id: `created-${draft.start}`, start: Temporal.Instant.fromEpochMilliseconds(draft.start) .toZonedDateTimeISO(localTimezoneID) .toPlainDate(), title, } : { category: 'Sync', end: draft.end, id: `created-${draft.start}`, start: draft.start, title, }; setStoryEvents(currentEvents => [...currentEvents, createdEvent]); close(); }} /> ), snapMinutes: 15, });
return ( <Schedule categories={categories} events={storyEvents} highlightDate={defaultHighlightDate} plugins={[paginationPlugin, createPlugin, popoverPlugin]} timezoneID={localTimezoneID} view={view} viewDate={viewDate} /> );}
function CreateEventForm({ draft, hasDetails = false, onCancel, onCreate,}: { draft: ScheduleEventDraft; /** * Adds location, attendee, and notes fields, making the popover taller than * the space above or below most drafted events. */ hasDetails?: boolean; onCancel: () => void; onCreate: (title: string) => void;}): React.JSX.Element { const [title, setTitle] = useState(''); const [location, setLocation] = useState(''); const [attendees, setAttendees] = useState(''); const [notes, setNotes] = useState(''); const timeLabel = draft.isAllDay ? `${Temporal.Instant.fromEpochMilliseconds(draft.start) .toZonedDateTimeISO(localTimezoneID) .toLocaleString(undefined, { day: 'numeric', month: 'short', weekday: 'short', })} · All day` : `${Temporal.Instant.fromEpochMilliseconds(draft.start) .toZonedDateTimeISO(localTimezoneID) .toLocaleString(undefined, { hour: 'numeric', minute: '2-digit', weekday: 'short', })} – ${Temporal.Instant.fromEpochMilliseconds(draft.end) .toZonedDateTimeISO(localTimezoneID) .toLocaleString(undefined, {hour: 'numeric', minute: '2-digit'})}`;
return ( <form className={css({w: 80})} onSubmit={submitEvent => { submitEvent.preventDefault(); onCreate(title.trim() === '' ? 'Untitled event' : title.trim()); }}> <Layout content={ <LayoutContent className={css({display: 'grid', gap: 3})}> <TextInput hasAutoFocus label="Title" onChange={setTitle} placeholder="Add a title" value={title} /> <Text color="secondary" type="supporting"> {timeLabel} </Text> {hasDetails ? ( <> <TextInput label="Location" onChange={setLocation} placeholder="Add a location" value={location} /> <TextInput label="Attendees" onChange={setAttendees} placeholder="Add attendees" value={attendees} /> <TextArea label="Notes" onChange={setNotes} placeholder="Add notes" rows={8} value={notes} /> </> ) : null} </LayoutContent> } footer={ <LayoutFooter primaryButton={ <Button label="Create" size="sm" type="submit" variant="primary" /> } secondaryButton={ <Button label="Cancel" onClick={onCancel} size="sm" variant="ghost" /> } /> } header={<LayoutHeader title="New event" />} height="auto" /> </form> );}
<CreatableEventsStory view={createScheduleMonthlyView()} />Monday Start Week
Section titled “Monday Start Week”October 2026
Mon12
Tue13
Wed14
Thu15
Fri16
Sat17
Sun18
function ScheduleStory({ categories: storyCategories = categories, eventBorderStyle, events: storyEvents = events, height, highlightDate = defaultHighlightDate, plugins, timezoneID = localTimezoneID, view, viewDate: initialViewDate = defaultViewDate,}: { categories?: ReadonlyArray<ScheduleCategory>; eventBorderStyle?: ScheduleEventBorderStyle; events?: ScheduleEventSource; height?: ScheduleHeight; highlightDate?: Instant; plugins?: SchedulePlugin[]; timezoneID?: string; view: ScheduleView; viewDate?: Instant;}) { const [viewDate, setViewDate] = useState<Instant>(() => initialViewDate); const paginationPlugin = useSchedulePaginationPlugin({ onViewDateChange: setViewDate, }); return ( <Schedule categories={storyCategories} eventBorderStyle={eventBorderStyle} events={storyEvents} height={height} highlightDate={highlightDate} plugins={plugins ?? [paginationPlugin]} timezoneID={timezoneID} view={view} viewDate={viewDate} /> );}
<ScheduleStory view={createScheduleWeeklyView({ maxHour: 18, minHour: 8, weekStartsOn: 1, })}/>Hidden Weekend Days
Section titled “Hidden Weekend Days”October 2026
Mon12
Tue13
Wed14
Thu15
Fri16
function ScheduleStory({ categories: storyCategories = categories, eventBorderStyle, events: storyEvents = events, height, highlightDate = defaultHighlightDate, plugins, timezoneID = localTimezoneID, view, viewDate: initialViewDate = defaultViewDate,}: { categories?: ReadonlyArray<ScheduleCategory>; eventBorderStyle?: ScheduleEventBorderStyle; events?: ScheduleEventSource; height?: ScheduleHeight; highlightDate?: Instant; plugins?: SchedulePlugin[]; timezoneID?: string; view: ScheduleView; viewDate?: Instant;}) { const [viewDate, setViewDate] = useState<Instant>(() => initialViewDate); const paginationPlugin = useSchedulePaginationPlugin({ onViewDateChange: setViewDate, }); return ( <Schedule categories={storyCategories} eventBorderStyle={eventBorderStyle} events={storyEvents} height={height} highlightDate={highlightDate} plugins={plugins ?? [paginationPlugin]} timezoneID={timezoneID} view={view} viewDate={viewDate} /> );}
<ScheduleStory events={weekEvents} view={createScheduleWeeklyView({ hiddenDays: [0, 6], maxHour: 18, minHour: 8, })}/>October 2026
Wed14
function ScheduleStory({ categories: storyCategories = categories, eventBorderStyle, events: storyEvents = events, height, highlightDate = defaultHighlightDate, plugins, timezoneID = localTimezoneID, view, viewDate: initialViewDate = defaultViewDate,}: { categories?: ReadonlyArray<ScheduleCategory>; eventBorderStyle?: ScheduleEventBorderStyle; events?: ScheduleEventSource; height?: ScheduleHeight; highlightDate?: Instant; plugins?: SchedulePlugin[]; timezoneID?: string; view: ScheduleView; viewDate?: Instant;}) { const [viewDate, setViewDate] = useState<Instant>(() => initialViewDate); const paginationPlugin = useSchedulePaginationPlugin({ onViewDateChange: setViewDate, }); return ( <Schedule categories={storyCategories} eventBorderStyle={eventBorderStyle} events={storyEvents} height={height} highlightDate={highlightDate} plugins={plugins ?? [paginationPlugin]} timezoneID={timezoneID} view={view} viewDate={viewDate} /> );}
<ScheduleStory view={createScheduleDayView({maxHour: 18, minHour: 8})} />Overlapping Events Side By Side
Section titled “Overlapping Events Side By Side”October 2026
Wed14
function ScheduleStory({ categories: storyCategories = categories, eventBorderStyle, events: storyEvents = events, height, highlightDate = defaultHighlightDate, plugins, timezoneID = localTimezoneID, view, viewDate: initialViewDate = defaultViewDate,}: { categories?: ReadonlyArray<ScheduleCategory>; eventBorderStyle?: ScheduleEventBorderStyle; events?: ScheduleEventSource; height?: ScheduleHeight; highlightDate?: Instant; plugins?: SchedulePlugin[]; timezoneID?: string; view: ScheduleView; viewDate?: Instant;}) { const [viewDate, setViewDate] = useState<Instant>(() => initialViewDate); const paginationPlugin = useSchedulePaginationPlugin({ onViewDateChange: setViewDate, }); return ( <Schedule categories={storyCategories} eventBorderStyle={eventBorderStyle} events={storyEvents} height={height} highlightDate={highlightDate} plugins={plugins ?? [paginationPlugin]} timezoneID={timezoneID} view={view} viewDate={viewDate} /> );}
<ScheduleStory events={overlappingEvents} view={createScheduleDayView({maxHour: 16, minHour: 8})}/>Overlapping Events Indented
Section titled “Overlapping Events Indented”October 2026
Wed14
function ScheduleStory({ categories: storyCategories = categories, eventBorderStyle, events: storyEvents = events, height, highlightDate = defaultHighlightDate, plugins, timezoneID = localTimezoneID, view, viewDate: initialViewDate = defaultViewDate,}: { categories?: ReadonlyArray<ScheduleCategory>; eventBorderStyle?: ScheduleEventBorderStyle; events?: ScheduleEventSource; height?: ScheduleHeight; highlightDate?: Instant; plugins?: SchedulePlugin[]; timezoneID?: string; view: ScheduleView; viewDate?: Instant;}) { const [viewDate, setViewDate] = useState<Instant>(() => initialViewDate); const paginationPlugin = useSchedulePaginationPlugin({ onViewDateChange: setViewDate, }); return ( <Schedule categories={storyCategories} eventBorderStyle={eventBorderStyle} events={storyEvents} height={height} highlightDate={highlightDate} plugins={plugins ?? [paginationPlugin]} timezoneID={timezoneID} view={view} viewDate={viewDate} /> );}
<ScheduleStory events={overlappingEvents} view={createScheduleDayView({ maxHour: 16, minHour: 8, overlapBehavior: 'indented', })}/>List View
Section titled “List View”October 2026
14Wed
15Thu
function ScheduleStory({ categories: storyCategories = categories, eventBorderStyle, events: storyEvents = events, height, highlightDate = defaultHighlightDate, plugins, timezoneID = localTimezoneID, view, viewDate: initialViewDate = defaultViewDate,}: { categories?: ReadonlyArray<ScheduleCategory>; eventBorderStyle?: ScheduleEventBorderStyle; events?: ScheduleEventSource; height?: ScheduleHeight; highlightDate?: Instant; plugins?: SchedulePlugin[]; timezoneID?: string; view: ScheduleView; viewDate?: Instant;}) { const [viewDate, setViewDate] = useState<Instant>(() => initialViewDate); const paginationPlugin = useSchedulePaginationPlugin({ onViewDateChange: setViewDate, }); return ( <Schedule categories={storyCategories} eventBorderStyle={eventBorderStyle} events={storyEvents} height={height} highlightDate={highlightDate} plugins={plugins ?? [paginationPlugin]} timezoneID={timezoneID} view={view} viewDate={viewDate} /> );}
() => { const now = Temporal.Now.instant(); const timezoneID = Intl.DateTimeFormat().resolvedOptions().timeZone; const listEvents = [ createEventFromISO({ category: 'Sync', end: now.subtract({minutes: 90}).toString(), id: 'list-past-sync', start: now.subtract({minutes: 150}).toString(), title: 'Past sync', }), createEventFromISO({ category: 'Customer', end: now.subtract({minutes: 20}).toString(), id: 'list-past-escalation', start: now.subtract({minutes: 80}).toString(), title: 'Customer escalation review', }), createEventFromISO({ category: 'Design', end: now.add({minutes: 45}).toString(), id: 'list-current-review', start: now.subtract({minutes: 15}).toString(), title: 'Current design review', }), createEventFromISO({ category: 'Planning', end: now.add({minutes: 135}).toString(), id: 'list-future-planning', start: now.add({minutes: 75}).toString(), title: 'Future planning session', }), createEventFromISO({ category: 'Operations', end: now.add({minutes: 240}).toString(), id: 'list-future-release', start: now.add({minutes: 180}).toString(), title: 'Release readiness', }), ];
return ( <ScheduleStory events={listEvents} highlightDate={now.epochMilliseconds} timezoneID={timezoneID} view={createScheduleListView({days: 7})} viewDate={now.epochMilliseconds} /> );}Async Events
Section titled “Async Events”October 2026
Sun11
Mon12
Tue13
Wed14
Thu15
Fri16
Sat17
function ScheduleStory({ categories: storyCategories = categories, eventBorderStyle, events: storyEvents = events, height, highlightDate = defaultHighlightDate, plugins, timezoneID = localTimezoneID, view, viewDate: initialViewDate = defaultViewDate,}: { categories?: ReadonlyArray<ScheduleCategory>; eventBorderStyle?: ScheduleEventBorderStyle; events?: ScheduleEventSource; height?: ScheduleHeight; highlightDate?: Instant; plugins?: SchedulePlugin[]; timezoneID?: string; view: ScheduleView; viewDate?: Instant;}) { const [viewDate, setViewDate] = useState<Instant>(() => initialViewDate); const paginationPlugin = useSchedulePaginationPlugin({ onViewDateChange: setViewDate, }); return ( <Schedule categories={storyCategories} eventBorderStyle={eventBorderStyle} events={storyEvents} height={height} highlightDate={highlightDate} plugins={plugins ?? [paginationPlugin]} timezoneID={timezoneID} view={view} viewDate={viewDate} /> );}
() => { const loader = useMemo<ScheduleEventSource>( () => async () => new Promise(resolve => { setTimeout(() => resolve(events), 900); }), [], );
return ( <ScheduleStory events={loader} view={createScheduleWeeklyView({maxHour: 18, minHour: 8})} /> );}Async Events With Toast Error
Section titled “Async Events With Toast Error”October 2026
Sun11
Mon12
Tue13
Wed14
Thu15
Fri16
Sat17
function AsyncEventsWithToastStory(): React.JSX.Element { const toast = useToast(); const loader = useMemo<ScheduleEventSource>( () => async () => { try { await new Promise((_, reject) => { setTimeout(() => reject(new Error('Unable to load events')), 900); }); return []; } catch { toast({ body: 'Unable to load schedule events', type: 'error', uniqueID: 'schedule-events-load-error', }); return []; } }, [toast], );
return ( <ScheduleStory events={loader} view={createScheduleWeeklyView({maxHour: 18, minHour: 8})} /> );}
function ScheduleStory({ categories: storyCategories = categories, eventBorderStyle, events: storyEvents = events, height, highlightDate = defaultHighlightDate, plugins, timezoneID = localTimezoneID, view, viewDate: initialViewDate = defaultViewDate,}: { categories?: ReadonlyArray<ScheduleCategory>; eventBorderStyle?: ScheduleEventBorderStyle; events?: ScheduleEventSource; height?: ScheduleHeight; highlightDate?: Instant; plugins?: SchedulePlugin[]; timezoneID?: string; view: ScheduleView; viewDate?: Instant;}) { const [viewDate, setViewDate] = useState<Instant>(() => initialViewDate); const paginationPlugin = useSchedulePaginationPlugin({ onViewDateChange: setViewDate, }); return ( <Schedule categories={storyCategories} eventBorderStyle={eventBorderStyle} events={storyEvents} height={height} highlightDate={highlightDate} plugins={plugins ?? [paginationPlugin]} timezoneID={timezoneID} view={view} viewDate={viewDate} /> );}
<ToastViewport> <AsyncEventsWithToastStory /></ToastViewport>View Selector
Section titled “View Selector”October 2026
Sun11
Mon12
Tue13
Wed14
Thu15
Fri16
Sat17
function ScheduleStory({ categories: storyCategories = categories, eventBorderStyle, events: storyEvents = events, height, highlightDate = defaultHighlightDate, plugins, timezoneID = localTimezoneID, view, viewDate: initialViewDate = defaultViewDate,}: { categories?: ReadonlyArray<ScheduleCategory>; eventBorderStyle?: ScheduleEventBorderStyle; events?: ScheduleEventSource; height?: ScheduleHeight; highlightDate?: Instant; plugins?: SchedulePlugin[]; timezoneID?: string; view: ScheduleView; viewDate?: Instant;}) { const [viewDate, setViewDate] = useState<Instant>(() => initialViewDate); const paginationPlugin = useSchedulePaginationPlugin({ onViewDateChange: setViewDate, }); return ( <Schedule categories={storyCategories} eventBorderStyle={eventBorderStyle} events={storyEvents} height={height} highlightDate={highlightDate} plugins={plugins ?? [paginationPlugin]} timezoneID={timezoneID} view={view} viewDate={viewDate} /> );}
() => { const views = useMemo( (): ScheduleViewSelectorOption[] => [ {hotkey: 'm', label: 'Month', view: createScheduleMonthlyView()}, { hotkey: 'w', label: 'Week', view: createScheduleWeeklyView({maxHour: 18, minHour: 8}), }, { hotkey: 'd', label: 'Day', view: createScheduleDayView({maxHour: 18, minHour: 8}), }, {hotkey: 'l', label: 'List', view: createScheduleListView({days: 7})}, ], [], ); const [view, setView] = useState<ScheduleView>(views[1].view); const viewSelectorPlugin = useScheduleViewSelectorPlugin(views, { onChangeView: setView, });
return <ScheduleStory plugins={[viewSelectorPlugin]} view={view} />;}Fill Height
Section titled “Fill Height”October 2026
Sun11
Mon12
Tue13
Wed14
Thu15
Fri16
Sat17
() => { const views = useMemo( (): {label: string; view: ScheduleView}[] => [ {label: 'Month', view: createScheduleMonthlyView()}, { label: 'Week', view: createScheduleWeeklyView({maxHour: 18, minHour: 8}), }, {label: 'Day', view: createScheduleDayView({maxHour: 18, minHour: 8})}, {label: 'List', view: createScheduleListView({days: 7})}, ], [], ); const [view, setView] = useState<ScheduleView>(views[1].view); const [viewDate, setViewDate] = useState<Instant>(() => defaultViewDate); const paginationPlugin = useSchedulePaginationPlugin({ onViewDateChange: setViewDate, }); const viewSelectorPlugin = useScheduleViewSelectorPlugin(views, { onChangeView: setView, });
return ( <div className={fillHeightStoryContainer}> <Schedule categories={categories} events={events} height="fill" highlightDate={defaultHighlightDate} plugins={[paginationPlugin, viewSelectorPlugin]} timezoneID={localTimezoneID} view={view} viewDate={viewDate} /> </div> );}Fill Height With Surplus Space
Section titled “Fill Height With Surplus Space”October 2026
Sun11
Mon12
Tue13
Wed14
Thu15
Fri16
Sat17
function ScheduleStory({ categories: storyCategories = categories, eventBorderStyle, events: storyEvents = events, height, highlightDate = defaultHighlightDate, plugins, timezoneID = localTimezoneID, view, viewDate: initialViewDate = defaultViewDate,}: { categories?: ReadonlyArray<ScheduleCategory>; eventBorderStyle?: ScheduleEventBorderStyle; events?: ScheduleEventSource; height?: ScheduleHeight; highlightDate?: Instant; plugins?: SchedulePlugin[]; timezoneID?: string; view: ScheduleView; viewDate?: Instant;}) { const [viewDate, setViewDate] = useState<Instant>(() => initialViewDate); const paginationPlugin = useSchedulePaginationPlugin({ onViewDateChange: setViewDate, }); return ( <Schedule categories={storyCategories} eventBorderStyle={eventBorderStyle} events={storyEvents} height={height} highlightDate={highlightDate} plugins={plugins ?? [paginationPlugin]} timezoneID={timezoneID} view={view} viewDate={viewDate} /> );}
<div className={fillHeightStoryContainer}> <ScheduleStory height="fill" view={createScheduleWeeklyView({maxHour: 10, minHour: 8})} /></div>Fill Height Scrolled To Now
Section titled “Fill Height Scrolled To Now”October 2026
Sun11
Mon12
Tue13
Wed14
Thu15
Fri16
Sat17
function ScheduleStory({ categories: storyCategories = categories, eventBorderStyle, events: storyEvents = events, height, highlightDate = defaultHighlightDate, plugins, timezoneID = localTimezoneID, view, viewDate: initialViewDate = defaultViewDate,}: { categories?: ReadonlyArray<ScheduleCategory>; eventBorderStyle?: ScheduleEventBorderStyle; events?: ScheduleEventSource; height?: ScheduleHeight; highlightDate?: Instant; plugins?: SchedulePlugin[]; timezoneID?: string; view: ScheduleView; viewDate?: Instant;}) { const [viewDate, setViewDate] = useState<Instant>(() => initialViewDate); const paginationPlugin = useSchedulePaginationPlugin({ onViewDateChange: setViewDate, }); return ( <Schedule categories={storyCategories} eventBorderStyle={eventBorderStyle} events={storyEvents} height={height} highlightDate={highlightDate} plugins={plugins ?? [paginationPlugin]} timezoneID={timezoneID} view={view} viewDate={viewDate} /> );}
<div className={fillHeightStoryContainer}> <ScheduleStory height="fill" view={createScheduleWeeklyView({initialScrollTime: 'now'})} /></div>Custom Plugin
Section titled “Custom Plugin”October 2026
Sun11
Mon12
Tue13
Wed14
Thu15
Fri16
Sat17
function ScheduleStory({ categories: storyCategories = categories, eventBorderStyle, events: storyEvents = events, height, highlightDate = defaultHighlightDate, plugins, timezoneID = localTimezoneID, view, viewDate: initialViewDate = defaultViewDate,}: { categories?: ReadonlyArray<ScheduleCategory>; eventBorderStyle?: ScheduleEventBorderStyle; events?: ScheduleEventSource; height?: ScheduleHeight; highlightDate?: Instant; plugins?: SchedulePlugin[]; timezoneID?: string; view: ScheduleView; viewDate?: Instant;}) { const [viewDate, setViewDate] = useState<Instant>(() => initialViewDate); const paginationPlugin = useSchedulePaginationPlugin({ onViewDateChange: setViewDate, }); return ( <Schedule categories={storyCategories} eventBorderStyle={eventBorderStyle} events={storyEvents} height={height} highlightDate={highlightDate} plugins={plugins ?? [paginationPlugin]} timezoneID={timezoneID} view={view} viewDate={viewDate} /> );}
() => { const customPlugin = useMemo<SchedulePlugin>( () => ({ renderHeader(startContent, centerContent, endContent) { return { centerContent, endContent: ( <> {endContent} <Button label="Export" /> </> ), startContent: ( <> {startContent} <Badge color="info" label="Draft" size="sm" /> </> ), }; }, }), [], );
return ( <ScheduleStory plugins={[customPlugin]} view={createScheduleWeeklyView({maxHour: 18, minHour: 8})} /> );}Event End Content Plugin
Section titled “Event End Content Plugin”October 2026
Sun
Mon
Tue
Wed
Thu
Fri
Sat
function ScheduleStory({ categories: storyCategories = categories, eventBorderStyle, events: storyEvents = events, height, highlightDate = defaultHighlightDate, plugins, timezoneID = localTimezoneID, view, viewDate: initialViewDate = defaultViewDate,}: { categories?: ReadonlyArray<ScheduleCategory>; eventBorderStyle?: ScheduleEventBorderStyle; events?: ScheduleEventSource; height?: ScheduleHeight; highlightDate?: Instant; plugins?: SchedulePlugin[]; timezoneID?: string; view: ScheduleView; viewDate?: Instant;}) { const [viewDate, setViewDate] = useState<Instant>(() => initialViewDate); const paginationPlugin = useSchedulePaginationPlugin({ onViewDateChange: setViewDate, }); return ( <Schedule categories={storyCategories} eventBorderStyle={eventBorderStyle} events={storyEvents} height={height} highlightDate={highlightDate} plugins={plugins ?? [paginationPlugin]} timezoneID={timezoneID} view={view} viewDate={viewDate} /> );}
() => { const views = useMemo( (): ScheduleViewSelectorOption[] => [ {label: 'Month', view: createScheduleMonthlyView()}, { label: 'Week', view: createScheduleWeeklyView({maxHour: 18, minHour: 8}), }, {label: 'List', view: createScheduleListView({days: 7})}, ], [], ); const [view, setView] = useState<ScheduleView>(views[0].view); const viewSelectorPlugin = useScheduleViewSelectorPlugin(views, { onChangeView: setView, }); const statusPlugin = useMemo<SchedulePlugin>( () => ({ renderEventEndContent: ({event}) => { if (event.id === 'customer-escalation') { return ( <Tooltip content="Needs attention" hoverIndication="never"> <span aria-label="Needs attention" role="img"> <Icon color="warning" icon={TriangleAlert} size="sm" /> </span> </Tooltip> ); } return event.id === 'offsite' ? ( <Tooltip content="Confirmed" hoverIndication="never"> <span aria-label="Confirmed" role="img"> <Icon color="success" icon={CircleCheck} size="sm" /> </span> </Tooltip> ) : null; }, }), [], ); const recurrencePlugin = useMemo<SchedulePlugin>( () => ({ renderEventEndContent: ({event}) => event.category === 'Planning' ? ( <Tooltip content="Recurring" hoverIndication="never"> <span aria-label="Recurring" role="img"> <Icon icon={Repeat2} size="sm" /> </span> </Tooltip> ) : null, }), [], );
return ( <ScheduleStory plugins={[viewSelectorPlugin, statusPlugin, recurrencePlugin]} view={view} /> );}Event Popover
Section titled “Event Popover”October 2026
Sun
Mon
Tue
Wed
Thu
Fri
Sat
function EventPopoverStory(): React.JSX.Element { const views = useMemo( (): {label: string; view: ScheduleView}[] => [ {label: 'Month', view: createScheduleMonthlyView()}, { label: 'Week', view: createScheduleWeeklyView({maxHour: 18, minHour: 8}), }, {label: 'Day', view: createScheduleDayView({maxHour: 18, minHour: 8})}, {label: 'List', view: createScheduleListView({days: 7})}, ], [], ); const [view, setView] = useState<ScheduleView>(views[0].view); const [viewDate, setViewDate] = useState<Instant>(() => defaultViewDate); const paginationPlugin = useSchedulePaginationPlugin({ onViewDateChange: setViewDate, }); const viewSelectorPlugin = useScheduleViewSelectorPlugin(views, { onChangeView: setView, }); const eventPopoverPlugin = useScheduleEventPopoverPlugin({ renderContent: ({event, close}) => ( <ScheduleEventPopoverContent event={event} onClose={close} onDelete={e => window.alert(`Delete ${e.title}`)} onEdit={e => window.alert(`Edit ${e.title}`)} onRespond={(e, response) => window.alert(`${response} for ${e.title}`)} /> ), }); return ( <Schedule categories={categories} events={eventPopoverEvents} highlightDate={defaultHighlightDate} plugins={[paginationPlugin, viewSelectorPlugin, eventPopoverPlugin]} timezoneID={localTimezoneID} view={view} viewDate={viewDate} /> );}
<EventPopoverStory />Event Popover Custom Content
Section titled “Event Popover Custom Content”October 2026
Sun11
Mon12
Tue13
Wed14
Thu15
Fri16
Sat17
() => { const [viewDate, setViewDate] = useState<Instant>(() => defaultViewDate); const paginationPlugin = useSchedulePaginationPlugin({ onViewDateChange: setViewDate, }); const eventPopoverPlugin = useScheduleEventPopoverPlugin({ hasCloseButton: true, renderContent: ({event}) => ( <Layout className={css({w: '64'})} content={ <LayoutContent padding={4}> <Badge color="info" label={event.category ?? 'Event'} size="sm" /> </LayoutContent> } header={<LayoutHeader title={event.title} />} height="auto" /> ), }); return ( <Schedule categories={categories} events={eventPopoverEvents} highlightDate={defaultHighlightDate} plugins={[paginationPlugin, eventPopoverPlugin]} timezoneID={localTimezoneID} view={createScheduleWeeklyView({maxHour: 18, minHour: 8})} viewDate={viewDate} /> );}Without Categories
Section titled “Without Categories”October 2026
Sun11
Mon12
Tue13
Wed14
Thu15
Fri16
Sat17
function ScheduleStory({ categories: storyCategories = categories, eventBorderStyle, events: storyEvents = events, height, highlightDate = defaultHighlightDate, plugins, timezoneID = localTimezoneID, view, viewDate: initialViewDate = defaultViewDate,}: { categories?: ReadonlyArray<ScheduleCategory>; eventBorderStyle?: ScheduleEventBorderStyle; events?: ScheduleEventSource; height?: ScheduleHeight; highlightDate?: Instant; plugins?: SchedulePlugin[]; timezoneID?: string; view: ScheduleView; viewDate?: Instant;}) { const [viewDate, setViewDate] = useState<Instant>(() => initialViewDate); const paginationPlugin = useSchedulePaginationPlugin({ onViewDateChange: setViewDate, }); return ( <Schedule categories={storyCategories} eventBorderStyle={eventBorderStyle} events={storyEvents} height={height} highlightDate={highlightDate} plugins={plugins ?? [paginationPlugin]} timezoneID={timezoneID} view={view} viewDate={viewDate} /> );}
<ScheduleStory categories={[]} events={eventsWithoutCategories} view={createScheduleWeeklyView({maxHour: 18, minHour: 8})}/>Color And Category Fallbacks
Section titled “Color And Category Fallbacks”October 2026
Sun11
Mon12
Tue13
Wed14
Thu15
Fri16
Sat17
function ScheduleStory({ categories: storyCategories = categories, eventBorderStyle, events: storyEvents = events, height, highlightDate = defaultHighlightDate, plugins, timezoneID = localTimezoneID, view, viewDate: initialViewDate = defaultViewDate,}: { categories?: ReadonlyArray<ScheduleCategory>; eventBorderStyle?: ScheduleEventBorderStyle; events?: ScheduleEventSource; height?: ScheduleHeight; highlightDate?: Instant; plugins?: SchedulePlugin[]; timezoneID?: string; view: ScheduleView; viewDate?: Instant;}) { const [viewDate, setViewDate] = useState<Instant>(() => initialViewDate); const paginationPlugin = useSchedulePaginationPlugin({ onViewDateChange: setViewDate, }); return ( <Schedule categories={storyCategories} eventBorderStyle={eventBorderStyle} events={storyEvents} height={height} highlightDate={highlightDate} plugins={plugins ?? [paginationPlugin]} timezoneID={timezoneID} view={view} viewDate={viewDate} /> );}
() => { const fallbackEvents = [ ...colorFallbackCategories.map((category, index) => createEventFromISO({ category: category.label, end: timedISO(0, index + 9, 30), id: `color-${category.color}`, start: timedISO(0, index + 9), title: `${category.label} category`, }), ), createEventFromISO({ category: 'Unmapped', end: timedISO(1, 15, 30), id: 'unknown-category', start: timedISO(1, 15), title: 'Unknown category keeps label', }), createEventFromISO({ end: timedISO(1, 16, 30), id: 'missing-category', start: timedISO(1, 16), title: 'Missing category uses default', }), ];
return ( <ScheduleStory categories={colorFallbackCategories} events={fallbackEvents} view={createScheduleWeeklyView({maxHour: 20, minHour: 8})} /> );}October 2026
14Wed
function ScheduleStory({ categories: storyCategories = categories, eventBorderStyle, events: storyEvents = events, height, highlightDate = defaultHighlightDate, plugins, timezoneID = localTimezoneID, view, viewDate: initialViewDate = defaultViewDate,}: { categories?: ReadonlyArray<ScheduleCategory>; eventBorderStyle?: ScheduleEventBorderStyle; events?: ScheduleEventSource; height?: ScheduleHeight; highlightDate?: Instant; plugins?: SchedulePlugin[]; timezoneID?: string; view: ScheduleView; viewDate?: Instant;}) { const [viewDate, setViewDate] = useState<Instant>(() => initialViewDate); const paginationPlugin = useSchedulePaginationPlugin({ onViewDateChange: setViewDate, }); return ( <Schedule categories={storyCategories} eventBorderStyle={eventBorderStyle} events={storyEvents} height={height} highlightDate={highlightDate} plugins={plugins ?? [paginationPlugin]} timezoneID={timezoneID} view={view} viewDate={viewDate} /> );}
<ScheduleStory events={[]} view={createScheduleListView({days: 7})} />Multi Day Events
Section titled “Multi Day Events”October 2026
Sun11
Mon12
Tue13
Wed14
Thu15
Fri16
Sat17
function ScheduleStory({ categories: storyCategories = categories, eventBorderStyle, events: storyEvents = events, height, highlightDate = defaultHighlightDate, plugins, timezoneID = localTimezoneID, view, viewDate: initialViewDate = defaultViewDate,}: { categories?: ReadonlyArray<ScheduleCategory>; eventBorderStyle?: ScheduleEventBorderStyle; events?: ScheduleEventSource; height?: ScheduleHeight; highlightDate?: Instant; plugins?: SchedulePlugin[]; timezoneID?: string; view: ScheduleView; viewDate?: Instant;}) { const [viewDate, setViewDate] = useState<Instant>(() => initialViewDate); const paginationPlugin = useSchedulePaginationPlugin({ onViewDateChange: setViewDate, }); return ( <Schedule categories={storyCategories} eventBorderStyle={eventBorderStyle} events={storyEvents} height={height} highlightDate={highlightDate} plugins={plugins ?? [paginationPlugin]} timezoneID={timezoneID} view={view} viewDate={viewDate} /> );}
<ScheduleStory events={multiDayEvents} view={createScheduleWeeklyView({maxHour: 24, minHour: 0})}/>Timezones
Section titled “Timezones”October 2026
Wed14
October 2026
Wed14
function ScheduleStory({ categories: storyCategories = categories, eventBorderStyle, events: storyEvents = events, height, highlightDate = defaultHighlightDate, plugins, timezoneID = localTimezoneID, view, viewDate: initialViewDate = defaultViewDate,}: { categories?: ReadonlyArray<ScheduleCategory>; eventBorderStyle?: ScheduleEventBorderStyle; events?: ScheduleEventSource; height?: ScheduleHeight; highlightDate?: Instant; plugins?: SchedulePlugin[]; timezoneID?: string; view: ScheduleView; viewDate?: Instant;}) { const [viewDate, setViewDate] = useState<Instant>(() => initialViewDate); const paginationPlugin = useSchedulePaginationPlugin({ onViewDateChange: setViewDate, }); return ( <Schedule categories={storyCategories} eventBorderStyle={eventBorderStyle} events={storyEvents} height={height} highlightDate={highlightDate} plugins={plugins ?? [paginationPlugin]} timezoneID={timezoneID} view={view} viewDate={viewDate} /> );}
() => { // Anchored to a fixed UTC time-of-day (not the viewer's local zone) so the // same instant is shown side by side in two zones deterministically. const utcToday = Temporal.Now.plainDateISO('UTC'); const utcInstant = (hour: number): string => utcToday .toZonedDateTime({ plainTime: Temporal.PlainTime.from({hour}), timeZone: 'UTC', }) .toInstant() .toString(); const sameInstantEvents = [ createEventFromISO({ category: 'Sync', end: utcInstant(19), id: 'global-sync', start: utcInstant(17), title: 'Global sync', }), ];
return ( <div style={{display: 'grid', gap: 24}}> <ScheduleStory events={sameInstantEvents} timezoneID="UTC" view={createScheduleDayView({maxHour: 20, minHour: 8})} viewDate={utcToday.toZonedDateTime('UTC').epochMilliseconds} /> <ScheduleStory events={sameInstantEvents} timezoneID="America/Los_Angeles" view={createScheduleDayView({maxHour: 20, minHour: 8})} viewDate={ utcToday.toZonedDateTime('America/Los_Angeles').epochMilliseconds } /> </div> );}Hour Range Edge Cases
Section titled “Hour Range Edge Cases”October 2026
Wed14
October 2026
Wed14
function ScheduleStory({ categories: storyCategories = categories, eventBorderStyle, events: storyEvents = events, height, highlightDate = defaultHighlightDate, plugins, timezoneID = localTimezoneID, view, viewDate: initialViewDate = defaultViewDate,}: { categories?: ReadonlyArray<ScheduleCategory>; eventBorderStyle?: ScheduleEventBorderStyle; events?: ScheduleEventSource; height?: ScheduleHeight; highlightDate?: Instant; plugins?: SchedulePlugin[]; timezoneID?: string; view: ScheduleView; viewDate?: Instant;}) { const [viewDate, setViewDate] = useState<Instant>(() => initialViewDate); const paginationPlugin = useSchedulePaginationPlugin({ onViewDateChange: setViewDate, }); return ( <Schedule categories={storyCategories} eventBorderStyle={eventBorderStyle} events={storyEvents} height={height} highlightDate={highlightDate} plugins={plugins ?? [paginationPlugin]} timezoneID={timezoneID} view={view} viewDate={viewDate} /> );}
<div style={{display: 'grid', gap: 24}}> <ScheduleStory view={createScheduleDayView({maxHour: 24, minHour: 0})} /> <ScheduleStory view={createScheduleDayView({maxHour: 24, minHour: 20})} /></div>Hour Heights
Section titled “Hour Heights”October 2026
Wed14
October 2026
Sun11
Mon12
Tue13
Wed14
Thu15
Fri16
Sat17
function ScheduleStory({ categories: storyCategories = categories, eventBorderStyle, events: storyEvents = events, height, highlightDate = defaultHighlightDate, plugins, timezoneID = localTimezoneID, view, viewDate: initialViewDate = defaultViewDate,}: { categories?: ReadonlyArray<ScheduleCategory>; eventBorderStyle?: ScheduleEventBorderStyle; events?: ScheduleEventSource; height?: ScheduleHeight; highlightDate?: Instant; plugins?: SchedulePlugin[]; timezoneID?: string; view: ScheduleView; viewDate?: Instant;}) { const [viewDate, setViewDate] = useState<Instant>(() => initialViewDate); const paginationPlugin = useSchedulePaginationPlugin({ onViewDateChange: setViewDate, }); return ( <Schedule categories={storyCategories} eventBorderStyle={eventBorderStyle} events={storyEvents} height={height} highlightDate={highlightDate} plugins={plugins ?? [paginationPlugin]} timezoneID={timezoneID} view={view} viewDate={viewDate} /> );}
<div style={{display: 'grid', gap: 24}}> <ScheduleStory view={createScheduleDayView({ hourHeight: 48, maxHour: 18, minHour: 8, })} /> <ScheduleStory view={createScheduleWeeklyView({ hourHeight: 132, maxHour: 18, minHour: 8, })} /></div>Current Time
Section titled “Current Time”October 2026
Wed14
function ScheduleStory({ categories: storyCategories = categories, eventBorderStyle, events: storyEvents = events, height, highlightDate = defaultHighlightDate, plugins, timezoneID = localTimezoneID, view, viewDate: initialViewDate = defaultViewDate,}: { categories?: ReadonlyArray<ScheduleCategory>; eventBorderStyle?: ScheduleEventBorderStyle; events?: ScheduleEventSource; height?: ScheduleHeight; highlightDate?: Instant; plugins?: SchedulePlugin[]; timezoneID?: string; view: ScheduleView; viewDate?: Instant;}) { const [viewDate, setViewDate] = useState<Instant>(() => initialViewDate); const paginationPlugin = useSchedulePaginationPlugin({ onViewDateChange: setViewDate, }); return ( <Schedule categories={storyCategories} eventBorderStyle={eventBorderStyle} events={storyEvents} height={height} highlightDate={highlightDate} plugins={plugins ?? [paginationPlugin]} timezoneID={timezoneID} view={view} viewDate={viewDate} /> );}
() => { const now = Temporal.Now.instant(); const currentEvents = [ createEventFromISO({ category: 'Sync', end: now.subtract({minutes: 30}).toString(), id: 'past-sync', start: now.subtract({minutes: 90}).toString(), title: 'Past sync', }), createEventFromISO({ category: 'Design', end: now.add({minutes: 90}).toString(), id: 'active-review', start: now.add({minutes: 30}).toString(), title: 'Design review', }), ];
return ( <ScheduleStory events={currentEvents} highlightDate={now.epochMilliseconds} view={createScheduleDayView({maxHour: 24, minHour: 0})} viewDate={now.epochMilliseconds} /> );}Current Time24 Hour Mode
Section titled “Current Time24 Hour Mode”October 2026
Wed14
function ScheduleStory({ categories: storyCategories = categories, eventBorderStyle, events: storyEvents = events, height, highlightDate = defaultHighlightDate, plugins, timezoneID = localTimezoneID, view, viewDate: initialViewDate = defaultViewDate,}: { categories?: ReadonlyArray<ScheduleCategory>; eventBorderStyle?: ScheduleEventBorderStyle; events?: ScheduleEventSource; height?: ScheduleHeight; highlightDate?: Instant; plugins?: SchedulePlugin[]; timezoneID?: string; view: ScheduleView; viewDate?: Instant;}) { const [viewDate, setViewDate] = useState<Instant>(() => initialViewDate); const paginationPlugin = useSchedulePaginationPlugin({ onViewDateChange: setViewDate, }); return ( <Schedule categories={storyCategories} eventBorderStyle={eventBorderStyle} events={storyEvents} height={height} highlightDate={highlightDate} plugins={plugins ?? [paginationPlugin]} timezoneID={timezoneID} view={view} viewDate={viewDate} /> );}
() => { const now = Temporal.Now.instant(); const timezoneID = Intl.DateTimeFormat().resolvedOptions().timeZone; const currentEvents = [ createEventFromISO({ category: 'Sync', end: now.subtract({minutes: 15}).toString(), id: 'current-24-past', start: now.subtract({minutes: 75}).toString(), title: 'Earlier sync', }), createEventFromISO({ category: 'Design', end: now.add({minutes: 90}).toString(), id: 'current-24-next', start: now.add({minutes: 30}).toString(), title: 'Upcoming review', }), ];
return ( <ScheduleStory events={currentEvents} highlightDate={now.epochMilliseconds} timezoneID={timezoneID} view={createScheduleDayView({ hourHeight: 56, maxHour: 24, minHour: 0, })} viewDate={now.epochMilliseconds} /> );}Past And Current States
Section titled “Past And Current States”October 2026
Wed14
function ScheduleStory({ categories: storyCategories = categories, eventBorderStyle, events: storyEvents = events, height, highlightDate = defaultHighlightDate, plugins, timezoneID = localTimezoneID, view, viewDate: initialViewDate = defaultViewDate,}: { categories?: ReadonlyArray<ScheduleCategory>; eventBorderStyle?: ScheduleEventBorderStyle; events?: ScheduleEventSource; height?: ScheduleHeight; highlightDate?: Instant; plugins?: SchedulePlugin[]; timezoneID?: string; view: ScheduleView; viewDate?: Instant;}) { const [viewDate, setViewDate] = useState<Instant>(() => initialViewDate); const paginationPlugin = useSchedulePaginationPlugin({ onViewDateChange: setViewDate, }); return ( <Schedule categories={storyCategories} eventBorderStyle={eventBorderStyle} events={storyEvents} height={height} highlightDate={highlightDate} plugins={plugins ?? [paginationPlugin]} timezoneID={timezoneID} view={view} viewDate={viewDate} /> );}
() => { const now = Temporal.Now.instant(); const stateEvents = [ createEventFromISO({ category: 'Sync', end: now.subtract({minutes: 45}).toString(), id: 'past-state', start: now.subtract({minutes: 105}).toString(), title: 'Past event', }), createEventFromISO({ category: 'Design', end: now.add({minutes: 30}).toString(), id: 'current-state', start: now.subtract({minutes: 30}).toString(), title: 'Current event', }), createEventFromISO({ category: 'Planning', end: now.add({minutes: 120}).toString(), id: 'future-state', start: now.add({minutes: 60}).toString(), title: 'Future event', }), ];
return ( <ScheduleStory events={stateEvents} highlightDate={now.epochMilliseconds} view={createScheduleDayView({maxHour: 24, minHour: 0})} viewDate={now.epochMilliseconds} /> );}Highlight Date
Section titled “Highlight Date”October 2026
Sun11
Mon12
Tue13
Wed14
Thu15
Fri16
Sat17
function ScheduleStory({ categories: storyCategories = categories, eventBorderStyle, events: storyEvents = events, height, highlightDate = defaultHighlightDate, plugins, timezoneID = localTimezoneID, view, viewDate: initialViewDate = defaultViewDate,}: { categories?: ReadonlyArray<ScheduleCategory>; eventBorderStyle?: ScheduleEventBorderStyle; events?: ScheduleEventSource; height?: ScheduleHeight; highlightDate?: Instant; plugins?: SchedulePlugin[]; timezoneID?: string; view: ScheduleView; viewDate?: Instant;}) { const [viewDate, setViewDate] = useState<Instant>(() => initialViewDate); const paginationPlugin = useSchedulePaginationPlugin({ onViewDateChange: setViewDate, }); return ( <Schedule categories={storyCategories} eventBorderStyle={eventBorderStyle} events={storyEvents} height={height} highlightDate={highlightDate} plugins={plugins ?? [paginationPlugin]} timezoneID={timezoneID} view={view} viewDate={viewDate} /> );}
<ScheduleStory highlightDate={dayInstant(2)} view={createScheduleWeeklyView({maxHour: 18, minHour: 8})} viewDate={dayInstant(0)}/>Highlighted Day Numbers
Section titled “Highlighted Day Numbers”Day 1
May 2026
Fri1
Day 2
May 2026
Sat2
Day 3
May 2026
Sun3
Day 4
May 2026
Mon4
Day 5
May 2026
Tue5
Day 6
May 2026
Wed6
Day 7
May 2026
Thu7
Day 8
May 2026
Fri8
Day 9
May 2026
Sat9
Day 10
May 2026
Sun10
Day 11
May 2026
Mon11
Day 12
May 2026
Tue12
Day 13
May 2026
Wed13
Day 14
May 2026
Thu14
Day 15
May 2026
Fri15
Day 16
May 2026
Sat16
Day 17
May 2026
Sun17
Day 18
May 2026
Mon18
Day 19
May 2026
Tue19
Day 20
May 2026
Wed20
Day 21
May 2026
Thu21
Day 22
May 2026
Fri22
Day 23
May 2026
Sat23
Day 24
May 2026
Sun24
Day 25
May 2026
Mon25
Day 26
May 2026
Tue26
Day 27
May 2026
Wed27
Day 28
May 2026
Thu28
Day 29
May 2026
Fri29
Day 30
May 2026
Sat30
Day 31
May 2026
Sun31
() => { const highlightedDays = Array.from({length: 31}, (_, index) => Temporal.PlainDate.from({ day: index + 1, month: 5, year: 2026, }).toZonedDateTime('UTC'), ); const view = createScheduleDayView({maxHour: 9, minHour: 8});
return ( <div className={highlightedDayNumberGrid}> {highlightedDays.map(day => { const instant = day.epochMilliseconds; return ( <div className={highlightedDayNumberExample} key={day.toString()}> <Text as="p" weight="bold"> Day {day.day} </Text> <Schedule events={[]} highlightDate={instant} plugins={[]} timezoneID="UTC" view={view} viewDate={instant} /> </div> ); })} </div> );}Automatic Month Rows
Section titled “Automatic Month Rows”July 2025
Sun
Mon
Tue
Wed
Thu
Fri
Sat
function ScheduleStory({ categories: storyCategories = categories, eventBorderStyle, events: storyEvents = events, height, highlightDate = defaultHighlightDate, plugins, timezoneID = localTimezoneID, view, viewDate: initialViewDate = defaultViewDate,}: { categories?: ReadonlyArray<ScheduleCategory>; eventBorderStyle?: ScheduleEventBorderStyle; events?: ScheduleEventSource; height?: ScheduleHeight; highlightDate?: Instant; plugins?: SchedulePlugin[]; timezoneID?: string; view: ScheduleView; viewDate?: Instant;}) { const [viewDate, setViewDate] = useState<Instant>(() => initialViewDate); const paginationPlugin = useSchedulePaginationPlugin({ onViewDateChange: setViewDate, }); return ( <Schedule categories={storyCategories} eventBorderStyle={eventBorderStyle} events={storyEvents} height={height} highlightDate={highlightDate} plugins={plugins ?? [paginationPlugin]} timezoneID={timezoneID} view={view} viewDate={viewDate} /> );}
() => { const viewDate = Temporal.PlainDate.from('2025-07-15').toZonedDateTime( 'UTC', ).epochMilliseconds; return ( <div className={automaticMonthRowsFillHeightContainer}> <ScheduleStory height="fill" highlightDate={viewDate} timezoneID="UTC" view={createScheduleMonthlyView()} viewDate={viewDate} /> </div> );}Show All Month Events
Section titled “Show All Month Events”October 2026
Sun
Mon
Tue
Wed
Thu
Fri
Sat
function ScheduleStory({ categories: storyCategories = categories, eventBorderStyle, events: storyEvents = events, height, highlightDate = defaultHighlightDate, plugins, timezoneID = localTimezoneID, view, viewDate: initialViewDate = defaultViewDate,}: { categories?: ReadonlyArray<ScheduleCategory>; eventBorderStyle?: ScheduleEventBorderStyle; events?: ScheduleEventSource; height?: ScheduleHeight; highlightDate?: Instant; plugins?: SchedulePlugin[]; timezoneID?: string; view: ScheduleView; viewDate?: Instant;}) { const [viewDate, setViewDate] = useState<Instant>(() => initialViewDate); const paginationPlugin = useSchedulePaginationPlugin({ onViewDateChange: setViewDate, }); return ( <Schedule categories={storyCategories} eventBorderStyle={eventBorderStyle} events={storyEvents} height={height} highlightDate={highlightDate} plugins={plugins ?? [paginationPlugin]} timezoneID={timezoneID} view={view} viewDate={viewDate} /> );}
<ScheduleStory events={busyDayEvents} view={createScheduleMonthlyView({monthRowHeight: 'auto'})}/>Schedule
Section titled “Schedule”Read-only schedule shell for day, week, month, and list views.
| Prop | Type | Default | Description |
|---|---|---|---|
categories | ReadonlyArray<ScheduleCategory> | — | Category definitions used to style events by their category value. |
className | string | — | Additional CSS class names applied to the schedule root. |
data-testid | string | — | Test ID applied to the schedule root. |
eventBorderStyle | "none" | "border" | "left" | 'border' | Border treatment applied to event pills. border renders the default border, none removes it, and left renders a square pill with only a 3px left border. |
events* | ScheduleEventSource | — | Static events or an async event loader. |
height | "fill" | "auto" | 'auto' | Height behavior. auto grows with the active view; fill occupies a finite-height parent and scrolls the view below the fixed toolbar. |
highlightDate | Instant | — | Instant highlighted as the current date. Defaults to mount time. |
plugins | ReadonlyArray<SchedulePlugin> | — | Header/rendering plugins. |
ref | Ref<HTMLDivElement> | — | Ref forwarded to the schedule root. |
style | CSSProperties | — | Inline styles applied to the schedule root. |
timezoneID | string | — | IANA timezone ID used for date math and rendering. |
view* | ScheduleView<Options> | — | View object returned by a createSchedule*View factory. |
viewDate* | Instant | — | Instant used to choose the rendered range. |
ScheduleEventPopoverContent
Section titled “ScheduleEventPopoverContent”Default Google-Calendar-style content for the event popover. Renders the event color, title, date/time, and category in a MetadataList with icon-only labels, plus optional `description` and `location` rows, an attendee response footer, and optional edit/delete actions — each shown only when the corresponding data or callback is present. A close button is always rendered. Pass this to the event popover plugin's `renderContent` to wire actions, e.g. `renderContent: (event, controls) => <ScheduleEventPopoverContent event={event} onClose={controls.close} onEdit={...} />`.
| Prop | Type | Default | Description |
|---|---|---|---|
event* | CalendarEvent<TAuxiliaryData> | — | The event to display. |
onClose* | () => void | — | Closes the popover. Wired to the close button. |
onDelete | (event: CalendarEvent<TAuxiliaryData>) => void | — | Called when the delete action is triggered. When omitted, no delete button is rendered. |
onEdit | (event: CalendarEvent<TAuxiliaryData>) => void | — | Called when the edit action is triggered. When omitted, no edit button is rendered. |
onRespond | ( event: CalendarEvent<TAuxiliaryData>, response: string | null, ) => void | — | Called when the attendee response (Going / Not Going / Maybe) changes. The response is null when the selection is cleared. |