Resource Calendar
A powerful calendar component for visualizing and managing events across multiple resources like rooms, equipment, or team members.
The Resource Calendar extends the standard calendar with resource-based event organization. Events are displayed in horizontal rows where each row represents a resource (person, room, equipment, etc.). It supports all standard calendar features including drag-and-drop, recurring events, and internationalization.
Basic Usage
Section titled “Basic Usage”Simple Resource Calendar
Section titled “Simple Resource Calendar”import { IlamyResourceCalendar } from '@ilamy/calendar';import type { Resource, CalendarEvent } from '@ilamy/calendar';import dayjs from 'dayjs';
const resources: Resource[] = [ { id: 'room-a', title: 'Conference Room A', color: '#3B82F6', backgroundColor: '#EFF6FF', }, { id: 'room-b', title: 'Conference Room B', color: '#EF4444', backgroundColor: '#FEF2F2', },];
const events: CalendarEvent[] = [ { id: 'event-1', title: 'Team Meeting', start: dayjs('2025-08-04T09:00:00.000Z'), end: dayjs('2025-08-04T10:00:00.000Z'), resourceId: 'room-a', // Assigned to Room A },];
function App() { return ( <IlamyResourceCalendar resources={resources} events={events} firstDayOfWeek="sunday" initialView="week" timeFormat="12-hour" /> );}Resource Interface
Section titled “Resource Interface”Resources represent the entities across which events are organized.
Resource Type Definition
Section titled “Resource Type Definition”interface Resource { // Unique identifier for the resource id: string | number;
// Display title of the resource title: string;
// Color for resource text (hex, rgb, or CSS class) color?: string;
// Background color for resource (hex, rgb, or CSS class) backgroundColor?: string;
// Optional position for resource display order position?: number;
// Optional business hours specific to this resource businessHours?: BusinessHours | BusinessHours[];
// Custom metadata (e.g., avatar URLs, roles, departments) // Accessible in custom resource renderers via renderResource data?: Record<string, any>;}Resource Color System
Section titled “Resource Color System”const resources: Resource[] = [ { id: 'designer', title: 'Design Team', color: '#8B5CF6', // Purple text backgroundColor: '#F5F3FF', // Light purple background }, { id: 'engineer', title: 'Engineering Team', color: '#10B981', // Green text backgroundColor: '#ECFDF5', // Light green background },];Resource Calendar Events
Section titled “Resource Calendar Events”The standard CalendarEvent interface includes optional resource assignment fields for use with resource calendars.
CalendarEvent with Resource Properties
Section titled “CalendarEvent with Resource Properties”interface CalendarEvent { // ... standard event properties (id, title, start, end, etc.)
// Single resource assignment resourceId?: string | number;
// Multiple resource assignment (cross-resource events) resourceIds?: (string | number)[];}Single Resource Events
Section titled “Single Resource Events”const event: CalendarEvent = { id: 'meeting-1', title: 'Team Standup', start: dayjs('2025-08-04T10:00:00.000Z'), end: dayjs('2025-08-04T10:30:00.000Z'), resourceId: 'room-a', // Assigned to one resource};Cross-Resource Events
Section titled “Cross-Resource Events”Events that span multiple resources using the resourceIds array:
const event: CalendarEvent = { id: 'all-hands', title: 'All Hands Meeting', start: dayjs('2025-08-04T14:00:00.000Z'), end: dayjs('2025-08-04T15:00:00.000Z'), resourceIds: ['room-a', 'room-b', 'room-c'], // Spans multiple resources color: '#8B5CF6',};The IlamyResourceCalendar component extends all props from IlamyCalendar (including locale, timezone, timeFormat, etc.) with resource-specific additions below.
Resource-Specific Props
Section titled “Resource-Specific Props”| Prop | Type | Default | Description |
|---|---|---|---|
resources | Resource[] | [] | Array of resources to display |
events | CalendarEvent[] | [] | Array of events with resource assignments (using resourceId or resourceIds properties) |
renderResource | (resource: Resource) => ReactNode | - | Custom function to render resource headers |
orientation | 'horizontal' | 'vertical' | 'horizontal' | Controls whether resources are displayed as rows (horizontal) or columns (vertical). |
weekViewGranularity | 'hourly' | 'daily' | 'hourly' | Week view density. 'hourly' (default) renders hourly time slots per day. 'daily' renders a single row per day per resource — useful for high-level, non-time-of-day schedules. See Week View Granularity below. |
hideNonBusinessHours | boolean | false | (Inherited) Whether to hide hours outside of business hours. |
hiddenDays | WeekDays[] | [] | (Inherited) Days to hide from the week view. Ignored in resource vertical week view with weekViewGranularity: 'daily' — non-contiguous visible days would break multi-day event positioning. Respected by the regular vertical week view and hourly resource vertical. |
renderCurrentTimeIndicator | (context: RenderCurrentTimeIndicatorProps) => ReactNode | - | (Inherited) Custom function to render the current time indicator. |
Examples
Section titled “Examples”Room Booking System
Section titled “Room Booking System”import { IlamyResourceCalendar } from '@ilamy/calendar';import type { CalendarEvent, CellClickInfo } from '@ilamy/calendar';import { useState } from 'react';import dayjs from 'dayjs';
const RoomBookingCalendar = () => { const [events, setEvents] = useState<CalendarEvent[]>([]);
const rooms: Resource[] = [ { id: 'conf-a', title: 'Conference Room A (10 people)', color: '#3B82F6', backgroundColor: '#EFF6FF', }, { id: 'conf-b', title: 'Conference Room B (20 people)', color: '#EF4444', backgroundColor: '#FEF2F2', }, { id: 'board-room', title: 'Board Room (8 people)', color: '#8B5CF6', backgroundColor: '#F5F3FF', }, ];
const handleCellClick = (info: CellClickInfo) => { const { start, end, resourceId } = info; if (!resourceId) return;
const newEvent: CalendarEvent = { id: `booking-${Date.now()}`, title: 'New Booking', start, end, uid: `booking-${Date.now()}@company.com`, resourceId, color: '#10B981', };
setEvents((prev) => [...prev, newEvent]); };
const handleEventUpdate = (event: CalendarEvent) => { setEvents((prev) => prev.map((e) => (e.id === event.id ? event : e)) ); };
return ( <IlamyResourceCalendar resources={rooms} events={events} initialView="week" onCellClick={handleCellClick} onEventUpdate={handleEventUpdate} firstDayOfWeek="monday" /> );};Custom Resource Rendering
Section titled “Custom Resource Rendering”Customize how resources are displayed using the renderResource prop:
import { Calendar, MapPin, Users } from 'lucide-react';
const IconResourceRenderer = (resource: Resource) => { const getResourceIcon = (resourceId: string) => { if (resourceId.includes('room')) return <MapPin className="h-4 w-4" />; if (resourceId.includes('team')) return <Users className="h-4 w-4" />; return <Calendar className="h-4 w-4" />; };
return ( <div className="flex items-center gap-2 p-2"> <div style={{ color: resource.color }}> {getResourceIcon(resource.id)} </div> <div className="flex flex-col"> <span className="font-medium">{resource.title}</span> <span className="text-xs" style={{ color: resource.color }}> Available </span> </div> </div> );};
function App() { return ( <IlamyResourceCalendar resources={resources} events={events} renderResource={IconResourceRenderer} /> );}Resource-Specific Business Hours
Section titled “Resource-Specific Business Hours”You can define working hours for individual resources. This is essential for managing shifts across a team or varying availability for different rooms or equipment.
Configuration
Section titled “Configuration”Resource-specific business hours are defined directly on the Resource object.
const resources: Resource[] = [ { id: 'dr-smith', title: 'Dr. Smith', businessHours: { daysOfWeek: ['monday', 'wednesday', 'friday'], startTime: 8, endTime: 16, } }, { id: 'dr-jones', title: 'Dr. Jones', businessHours: { daysOfWeek: ['tuesday', 'thursday'], startTime: 10, endTime: 18, } }];Precedence and Grid Behavior
Section titled “Precedence and Grid Behavior”- Grid Union: When
hideNonBusinessHoursis enabled, the calendar calculates the union of all business hours (global and resource-specific) to determine the visible range. If Dr. Smith works 8-16 and Dr. Jones works 10-18, the calendar will show the full 8-18 range. - Resource Shading: Each resource row (or column) will only enable the time slots that match its specific
businessHours. Slots outside its specific availability will be shaded/disabled. - Fallback: If a resource does not have specific
businessHoursdefined, it falls back to using the globalbusinessHoursconfiguration.
Automatic Form Validation
Section titled “Automatic Form Validation”The built-in EventForm is fully resource-aware. When you select a resource for an event:
- The DatePicker will automatically disable days that are not working days for that resource.
- The TimePicker will constrain the selectable time range to the resource’s specific working hours for that date.
The Resource Calendar supports three views, each displaying resources in horizontal rows.
Month View
Section titled “Month View”Timeline view with resources as rows and days as columns. Compact event display with scroll for all resources.
initialView="month"Week View
Section titled “Week View”Detailed 7-day timeline with hourly time slots. Perfect for precise scheduling with drag-and-drop between resources. Density is controlled via the weekViewGranularity prop ('hourly' by default, or 'daily' for a row-per-day schedule without time slots).
initialView="week"Day View
Section titled “Day View”Focused single-day view with maximum detail. Full hourly breakdown across all resources.
initialView="day"Week View Granularity
Section titled “Week View Granularity”The resource week view supports two density modes via the weekViewGranularity prop:
'hourly'(default): the week is broken into hourly time slots for each day. Best for precise, time-of-day scheduling — meetings, appointments, shifts at specific hours.'daily': each day gets a single row per resource with no time slots inside. Best for high-level schedules where only the day matters — availability boards, project assignments, leave calendars.
Hourly vs Daily — when to pick which
Section titled “Hourly vs Daily — when to pick which”<IlamyResourceCalendar resources={rooms} events={events} initialView="week" weekViewGranularity="hourly"/><IlamyResourceCalendar resources={teamMembers} events={availabilityEvents} initialView="week" weekViewGranularity="daily"/>Interactions with other props
Section titled “Interactions with other props”hiddenDaysis intentionally ignored whenweekViewGranularity="daily"is combined with vertical orientation. The daily mode assumes a contiguous week — hiding arbitrary days would break multi-day event positioning. If you need to hide days, stick withweekViewGranularity="hourly"or use the horizontal orientation.businessHoursandhideNonBusinessHoursstill apply in'daily'mode, but only influence day-level shading (non-business days are dimmed). They don’t affect the row structure since daily mode has no time slots to hide.- Drag-and-drop works in both modes. In
'daily', dropping an event on a different day updatesevent.startto that day at the same local time.
Best Practices
Section titled “Best Practices”Resource Organization
Section titled “Resource Organization”- Use meaningful resource IDs (e.g., ‘room-a’, ‘john-doe’)
- Set position property to control display order
- Group related resources by type
- Include capacity info in resource titles when relevant
Event Assignment
Section titled “Event Assignment”- Use resourceId for single resource events (simpler and more performant)
- Use resourceIds array only when event truly spans multiple resources
- Validate resource IDs exist in resources array
- Handle conflicts and validate resource availability before booking
Performance
Section titled “Performance”- Memoize event filtering for large datasets
- Consider virtualization for 50+ resources
- Batch multiple state updates together
- Use React.memo for custom resource renderers