Skip to content

Slots API

The Slots API lets you replace any sub-component with your own. Slot components receive typed props — build completely custom UIs while keeping the calendar’s state management, layout algorithms, and navigation.

Pass your custom components via the slots prop:

<Calendar
events={events}
slots={{
toolbar: MyCustomToolbar,
dayCell: MyCustomDayCell,
timeEvent: MyCustomTimeEvent,
allDayEvent: MyCustomAllDayEvent,
popover: MyCustomPopover,
agendaEvent: MyCustomAgendaEvent,
}}
/>
SlotPropsUsed in
toolbarToolbarSlotPropsAll views
dayCellDayCellSlotPropsMonth view
timeEventTimeEventSlotPropsWeek/Day view
allDayEventAllDayEventSlotPropsWeek/Day view
popoverPopoverSlotPropsAll views
agendaEventAgendaEventSlotPropsAgenda view
interface ToolbarSlotProps {
currentDate: DateString;
view: CalendarView;
onPrev: () => void;
onNext: () => void;
onToday: () => void;
onViewChange: (view: CalendarView) => void;
formattedDate: string; // Locale-aware title, e.g., "March 2026"
}
interface DayCellSlotProps {
date: DateString;
isToday: boolean;
isCurrentMonth: boolean;
events: CalendarEvent[];
}
interface TimeEventSlotProps {
event: CalendarEvent;
positioned: PositionedEvent;
// positioned.column: 0-based column index
// positioned.totalColumns: total columns in overlap group
// positioned.top: % position from top
// positioned.height: % height
}
interface AllDayEventSlotProps {
event: CalendarEvent;
segment: EventSegment;
// segment.date: the date this segment falls on
// segment.isStart: first day of the event
// segment.isEnd: last day of the event
}
interface PopoverSlotProps {
event: CalendarEvent;
onClose: () => void;
}
interface AgendaEventSlotProps {
event: CalendarEvent;
}
import type { ToolbarSlotProps } from "trud-calendar";
function MyToolbar({
formattedDate,
view,
onPrev,
onNext,
onToday,
onViewChange,
}: ToolbarSlotProps) {
return (
<div className="flex items-center justify-between p-4 border-b">
<div className="flex gap-2">
<button onClick={onPrev}>Back</button>
<button onClick={onToday}>Today</button>
<button onClick={onNext}>Forward</button>
</div>
<h2 className="text-lg font-bold">{formattedDate}</h2>
<select
value={view}
onChange={(e) => onViewChange(e.target.value as CalendarView)}
>
<option value="month">Month</option>
<option value="week">Week</option>
<option value="day">Day</option>
<option value="agenda">Agenda</option>
</select>
</div>
);
}
<Calendar events={events} slots={{ toolbar: MyToolbar }} />
import type { DayCellSlotProps } from "trud-calendar";
function MyDayCell({ date, isToday, isCurrentMonth, events }: DayCellSlotProps) {
return (
<div
className={`p-2 min-h-[80px] ${
isToday ? "bg-blue-50 border-blue-300" : ""
} ${!isCurrentMonth ? "opacity-30" : ""}`}
>
<div className="font-bold">{new Date(date).getDate()}</div>
{events.map((e) => (
<div key={e.id} className="text-xs truncate" style={{ color: e.color }}>
{e.title}
</div>
))}
</div>
);
}
import type { AgendaEventSlotProps } from "trud-calendar";
function MyAgendaEvent({ event }: AgendaEventSlotProps) {
return (
<div className="flex items-center gap-3 p-3 rounded-lg hover:bg-gray-50">
<div
className="w-3 h-3 rounded-full"
style={{ backgroundColor: event.color }}
/>
<div>
<div className="font-medium">{event.title}</div>
<div className="text-sm text-gray-500">
{event.description || "No description"}
</div>
</div>
</div>
);
}