Skip to content

Calendar

A comprehensive React calendar component with month, week, day, and year views, plus powerful event lifecycle callbacks.

The Calendar component is the main component for displaying calendar views. It supports various props for customization and event handling. Events should follow the CalendarEvent interface.

Basic Calendar
import { IlamyCalendar } from '@ilamy/calendar';
const events = [
{
id: '1',
title: 'Team Meeting',
start: new Date('2024-01-15T10:00:00'),
end: new Date('2024-01-15T11:00:00'),
description: 'Weekly team sync',
backgroundColor: '#3b82f6'
color: 'black'
},
{
id: '2',
title: 'Project Deadline',
start: new Date('2024-01-20T23:59:59'),
end: new Date('2024-01-20T23:59:59'),
allDay: true,
backgroundColor: '#ef4444'
color: 'black'
}
];
function MyCalendar() {
return (
<IlamyCalendar events={events} />
);
}
PropTypeDefaultDescription
eventsCalendarEvent[][]Array of events to display in the calendar
initialView'month' | 'week' | 'day' | 'year''month'Sets the initial view when the calendar loads
initialDatedayjs.Dayjs | Date | string | undefinedundefined (today’s date)Sets the initial date displayed when the calendar loads. When undefined, defaults to today’s date
firstDayOfWeek'sunday' | 'monday' | 'tuesday' | 'wednesday' | 'thursday' | 'friday' | 'saturday''sunday'The first day of the week to display
dayMaxEventsnumber4Maximum number of events to display in a day cell
renderEvent(event: CalendarEvent) => ReactNode-Custom function to render individual events
localestring'en'Locale for date formatting (e.g., “en”, “fr”, “de”)
timezonestringWhatever your local timezone isTimezone for date handling (e.g., “UTC”, “America/New_York”)
timeFormat'12-hour' | '24-hour''12-hour'Time format for displaying times in week and day views (e.g., ‘1:00 PM’ vs ‘13:00’)
stickyViewHeaderbooleantrueWhether to stick the view header to the top
viewHeaderClassNamestring''Custom class name for the view header
headerComponentReactNodenullCustom header component to render above the calendar
disableCellClickbooleanfalseDisable cell click interactions
disableEventClickbooleanfalseDisable event click interactions
disableDragAndDropbooleanfalseDisable drag-and-drop functionality for events
eventSpacingnumber2Spacing (in pixels) between events in calendar views. Allows customizable gaps between events for better visual clarity.
businessHoursBusinessHours | BusinessHours[]undefinedRestrict calendar interactions to specified days and time ranges. Users can only create and edit events within business hours. Supports single object or array for different hours per day.
renderEventForm(props: EventFormProps) => ReactNodeundefinedCustom function to render the event form for creating and editing events. When provided, replaces the default event form.
classesOverrideClassesOverrideundefinedCustom class names to override default styling for various calendar elements, including disabled cells.
translationsTranslations-Translations object for internationalization. If both translations and translator are provided, translator takes priority.
translatorTranslatorFunction-Translator function for internationalization. Takes priority over translations object if both are provided.
PropTypeDescription
onEventClick(event: CalendarEvent) => voidCalled when an event is clicked
onCellClick(info: CellClickInfo) => voidCalled when a date cell is clicked. Receives CellClickInfo object with start, end, and optional resourceId.
onViewChange(view: 'month' | 'week' | 'day' | 'year') => voidCalled when the calendar view changes
onEventAdd(event: CalendarEvent) => voidCalled when a new event is created
onEventUpdate(event: CalendarEvent) => voidCalled when an existing event is updated
onEventDelete(event: CalendarEvent) => voidCalled when an event is deleted
onDateChange(date: dayjs.Dayjs) => voidCalled when the calendar date changes
Event Handlers Example
import { IlamyCalendar } from '@ilamy/calendar';
function MyCalendar() {
const handleEventClick = (event) => {
console.log('Event clicked:', event);
// Open event details modal, navigate to event page, etc.
};
const handleDateClick = (info) => {
console.log('Date clicked:', info.start, info.end);
// Create new event, switch to day view, etc.
// info contains: { start: Dayjs, end: Dayjs, resourceId?: string | number }
};
const handleViewChange = (view) => {
console.log('View changed to:', view);
// Update URL, track analytics, etc.
};
return (
<IlamyCalendar
events={events}
firstDayOfWeek="monday"
onEventClick={handleEventClick}
onCellClick={handleDateClick}
onViewChange={handleViewChange}
/>
);
}

The calendar supports comprehensive internationalization including locale-based formatting, custom translations, and translator functions. For detailed documentation and examples, see the Internationalization guide.

Configure whether times are displayed in 12-hour or 24-hour format in week and day views.

12-Hour and 24-Hour Time Formats
import { IlamyCalendar } from '@ilamy/calendar';
function MyCalendar() {
return (
<>
{/* 12-hour format (default): displays as "1:00 PM", "2:30 PM" */}
<IlamyCalendar
events={events}
initialView="week"
timeFormat="12-hour"
/>
{/* 24-hour format: displays as "13:00", "14:30" */}
<IlamyCalendar
events={events}
initialView="week"
timeFormat="24-hour"
/>
</>
);
}
Custom Event Renderer
import { IlamyCalendar } from '@ilamy/calendar';
function CustomCalendar() {
const renderEvent = (event) => (
<div className="px-2 py-1 rounded bg-blue-100 text-blue-800">
<div className="font-semibold">{event.title}</div>
<div className="text-xs">{event.description}</div>
</div>
);
return (
<IlamyCalendar
events={events}
renderEvent={renderEvent}
onEventClick={(event) => alert(`Clicked: ${event.title}`)}
onCellClick={(info) => console.log('Selected date:', info.start, info.end)}
/>
);
}
Advanced Calendar Configuration
import { IlamyCalendar } from '@ilamy/calendar';
import { useState } from 'react';
function AdvancedCalendar() {
const [events, setEvents] = useState([]);
const handleEventAdd = (event) => {
console.log('New event added:', event);
setEvents(prev => [...prev, event]);
// Save to your backend API
};
const handleEventUpdate = (updatedEvent) => {
console.log('Event updated:', updatedEvent);
setEvents(prev =>
prev.map(event =>
event.id === updatedEvent.id ? updatedEvent : event
)
);
// Update in your backend API
};
const handleEventDelete = (deletedEvent) => {
console.log('Event deleted:', deletedEvent);
setEvents(prev =>
prev.filter(event => event.id !== deletedEvent.id)
);
// Delete from your backend API
};
const handleDateChange = (date) => {
console.log('Date changed to:', date.format('YYYY-MM-DD'));
// Load events for the new date range
// fetchEventsForDate(date);
};
return (
<IlamyCalendar
events={events}
initialView="week" // Start with week view
onEventAdd={handleEventAdd}
onEventUpdate={handleEventUpdate}
onEventDelete={handleEventDelete}
onDateChange={handleDateChange}
onEventClick={(event) => console.log('Event clicked:', event)}
onCellClick={(info) => console.log('Cell clicked:', info.start, info.end)}
/>
);
}

The calendar supports multiple view modes that you can set using the initialView prop.

Traditional monthly calendar showing the entire month at a glance.

<IlamyCalendar initialView="month" events={events} />

Weekly view showing 7 days with detailed time slots.

<IlamyCalendar initialView="week" events={events} />

Single day view with hourly time slots for detailed scheduling.

<IlamyCalendar initialView="day" events={events} />

Annual overview showing all 12 months for long-term planning.

<IlamyCalendar initialView="year" events={events} />
Setting Different Initial Views
// Month view (default)
<IlamyCalendar initialView="month" events={events} />
// Week view for detailed scheduling
<IlamyCalendar initialView="week" events={events} />
// Day view for hourly planning
<IlamyCalendar initialView="day" events={events} />
// Year view for long-term overview
<IlamyCalendar initialView="year" events={events} />

Restrict calendar interactions to specific days and time ranges. When configured, users can only create and edit events within business hours. This is particularly useful for appointment booking, scheduling applications, and enforcing availability constraints.

Configuring Business Hours
import { IlamyCalendar } from '@ilamy/calendar';
function BusinessCalendar() {
const events = [/* your events */];
return (
<IlamyCalendar
events={events}
businessHours={{
daysOfWeek: ['monday', 'tuesday', 'wednesday', 'thursday', 'friday'],
startTime: 9, // 9 AM
endTime: 17, // 5 PM
}}
/>
);
}

Array of day names that are considered business days. Users can only interact with the calendar on these days.

Type: string[] Valid values: 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday'

Hour when business hours begin (24-hour format, inclusive). Events cannot be created or modified before this time.

Type: number (0-23)

Hour when business hours end (24-hour format, exclusive). Events cannot be created or modified at or after this time.

Type: number (0-23)

Replace the default event form with your own custom implementation using the renderEventForm prop. This gives you full control over the event creation and editing experience.

Custom Event Form Implementation
import { IlamyCalendar, EventFormProps, CalendarEvent } from '@ilamy/calendar';
const CustomEventForm = ({
open,
selectedEvent,
onAdd,
onUpdate,
onDelete,
onClose,
}: EventFormProps) => {
if (!open) return null;
const handleSubmit = (formData: FormData) => {
const eventData: CalendarEvent = {
id: selectedEvent?.id || `event-${Date.now()}`,
title: formData.get('title') as string,
start: selectedEvent?.start || new Date(),
end: selectedEvent?.end || new Date(),
};
if (selectedEvent?.id) {
onUpdate?.(eventData);
} else {
onAdd?.(eventData);
}
onClose();
};
return (
<dialog open={open} className="modal">
<form onSubmit={(e) => {
e.preventDefault();
handleSubmit(new FormData(e.currentTarget));
}}>
<input
name="title"
defaultValue={selectedEvent?.title}
placeholder="Event title"
/>
<div className="actions">
{selectedEvent?.id && (
<button type="button" onClick={() => {
onDelete?.(selectedEvent);
onClose();
}}>Delete</button>
)}
<button type="button" onClick={onClose}>Cancel</button>
<button type="submit">
{selectedEvent?.id ? 'Update' : 'Create'}
</button>
</div>
</form>
</dialog>
);
};
function MyCalendar() {
return (
<IlamyCalendar
events={events}
renderEventForm={(props) => <CustomEventForm {...props} />}
/>
);
}
  • open - Boolean indicating if the form should be visible
  • selectedEvent - The event being edited (null for new events)
  • onAdd - Callback to create a new event
  • onUpdate - Callback to update an existing event
  • onDelete - Callback to delete an event
  • onClose - Callback to close the form (required)

Handle event creation, updates, and deletion with powerful callback functions. These callbacks are essential for maintaining your event data and integrating with backend APIs.

Triggered when a user creates a new event through the calendar interface.

Type: (event: CalendarEvent) => void

Called when an existing event is modified (e.g., dragged, resized, or edited).

Type: (event: CalendarEvent) => void

Invoked when a user deletes an event from the calendar.

Type: (event: CalendarEvent) => void

Fired when the user navigates to a different date or time period.

Type: (date: dayjs.Dayjs) => void

Full Event Lifecycle Management
import { IlamyCalendar } from '@ilamy/calendar';
import { useState } from 'react';
function CalendarWithAPI() {
const [events, setEvents] = useState([]);
const [loading, setLoading] = useState(false);
// Handle new event creation
const handleEventAdd = async (event) => {
setLoading(true);
try {
// Save to backend
const response = await fetch('/api/events', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(event)
});
if (response.ok) {
const savedEvent = await response.json();
setEvents(prev => [...prev, savedEvent]);
console.log('Event created successfully:', savedEvent);
}
} catch (error) {
console.error('Failed to create event:', error);
} finally {
setLoading(false);
}
};
// Handle event updates
const handleEventUpdate = async (updatedEvent) => {
setLoading(true);
try {
const response = await fetch('/api/events/' + updatedEvent.id, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(updatedEvent)
});
if (response.ok) {
setEvents(prev =>
prev.map(event =>
event.id === updatedEvent.id ? updatedEvent : event
)
);
console.log('Event updated successfully:', updatedEvent);
}
} catch (error) {
console.error('Failed to update event:', error);
} finally {
setLoading(false);
}
};
// Handle event deletion
const handleEventDelete = async (deletedEvent) => {
setLoading(true);
try {
const response = await fetch('/api/events/' + deletedEvent.id, {
method: 'DELETE'
});
if (response.ok) {
setEvents(prev =>
prev.filter(event => event.id !== deletedEvent.id)
);
console.log('Event deleted successfully:', deletedEvent);
}
} catch (error) {
console.error('Failed to delete event:', error);
} finally {
setLoading(false);
}
};
// Handle date navigation
const handleDateChange = async (date) => {
console.log('Navigating to:', date.format('YYYY-MM-DD'));
// Optionally load events for the new date range
const monthStart = date.startOf('month');
const monthEnd = date.endOf('month');
try {
const response = await fetch(
'/api/events?start=' + monthStart.toISOString() +
'&end=' + monthEnd.toISOString()
);
if (response.ok) {
const monthEvents = await response.json();
setEvents(monthEvents);
}
} catch (error) {
console.error('Failed to load events for date:', error);
}
};
return (
<div className="relative">
{loading && (
<div className="absolute inset-0 bg-white/50 flex items-center justify-center z-10">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600"></div>
</div>
)}
<IlamyCalendar
events={events}
initialView="month"
onEventAdd={handleEventAdd}
onEventUpdate={handleEventUpdate}
onEventDelete={handleEventDelete}
onDateChange={handleDateChange}
onEventClick={(event) => {
// Navigate to event details or open modal
console.log('Event clicked:', event);
}}
/>
</div>
);
}

The main event object used throughout the calendar. All events passed to the calendar should conform to this interface.

CalendarEvent Interface
interface CalendarEvent {
id: string; // Unique identifier for the event
title: string; // Display title of the event
start: Date; // Start date and time
end: Date; // End date and time
description?: string; // Optional description text
color?: string; // Optional text color (hex, rgb, named colors, class names)
backgroundColor?: string; // Optional background color (hex, rgb, named colors, class names)
allDay?: boolean; // Whether this is an all-day event
resourceId?: string | number; // Single resource assignment (for resource calendars)
resourceIds?: (string | number)[]; // Multiple resource assignment (for cross-resource events)
data?: Record<string, any>; // Optional custom data for additional properties
}
  • id - Must be unique across all events. Used for drag-and-drop and event updates.
  • title - The text displayed on the event in the calendar.
  • start - JavaScript Date object representing when the event begins.
  • end - JavaScript Date object representing when the event ends.
  • description - Additional text shown in tooltips or custom event renderers.
  • color - Background color for the event. Supports hex (#ff0000), rgb(255,0,0), or named colors (red).
  • allDay - When true, the event spans the entire day and ignores time components.
Various Event Types
// Regular timed event
const meeting = {
id: 'meeting-1',
title: 'Team Standup',
start: new Date('2024-01-15T09:00:00'),
end: new Date('2024-01-15T09:30:00'),
description: 'Daily team sync meeting',
color: '#3b82f6'
};
// All-day event
const holiday = {
id: 'holiday-1',
title: 'National Holiday',
start: new Date('2024-01-01'),
end: new Date('2024-01-01'),
allDay: true,
color: '#ef4444'
};
// Multi-day event
const conference = {
id: 'conf-1',
title: 'Tech Conference',
start: new Date('2024-01-20T08:00:00'),
end: new Date('2024-01-22T18:00:00'),
description: 'Annual technology conference',
color: '#10b981'
};

Configuration object for restricting calendar interactions to specific days and time ranges. Used with the businessHours prop. Supports both single object (same hours for all days) and array format (different hours per day).

BusinessHours Type Definition
interface BusinessHours {
// Array of day names considered business days
daysOfWeek: ('monday' | 'tuesday' | 'wednesday' | 'thursday' | 'friday' | 'saturday' | 'sunday')[];
// Hour when business hours start (0-23, inclusive)
startTime: number;
// Hour when business hours end (0-23, exclusive)
endTime: number;
}

Standard business hours (same for all days):

const businessHours: BusinessHours = {
daysOfWeek: ['monday', 'tuesday', 'wednesday', 'thursday', 'friday'],
startTime: 9,
endTime: 17,
};
<IlamyCalendar
events={events}
businessHours={businessHours}
/>

Different hours per day (array format):

const businessHours: BusinessHours[] = [
{
daysOfWeek: ['monday', 'wednesday', 'friday'],
startTime: 9,
endTime: 17,
},
{
daysOfWeek: ['tuesday', 'thursday'],
startTime: 10,
endTime: 18,
},
{
daysOfWeek: ['saturday'],
startTime: 11,
endTime: 15,
},
];
<IlamyCalendar
events={events}
businessHours={businessHours}
/>

Information passed to the onCellClick callback. Uses named properties for better extensibility and clarity.

CellClickInfo Type Definition
interface CellClickInfo {
// Start date/time of the clicked cell
start: dayjs.Dayjs;
// End date/time of the clicked cell
end: dayjs.Dayjs;
// Resource ID if clicking on a resource calendar cell (optional)
resourceId?: string | number;
}

Handle cell clicks with the new CellClickInfo object:

const handleCellClick = (info: CellClickInfo) => {
const { start, end, resourceId } = info;
console.log('Cell clicked from', start.format(), 'to', end.format());
// For resource calendars, resourceId will be available
if (resourceId) {
console.log('On resource:', resourceId);
}
// Create a new event
openEventForm({ start, end, resourceId });
};
<IlamyCalendar
events={events}
onCellClick={handleCellClick}
/>

Custom class names to override default styling for various calendar elements. Used with the classesOverride prop to customize the appearance of calendar components.

ClassesOverride Type Definition
interface ClassesOverride {
// Custom class name for disabled cells
// Applied to: cells outside business hours/days, and days from other months in month view
disabledCell?: string;
}

Customize the styling of disabled cells:

const classesOverride: ClassesOverride = {
disabledCell: 'opacity-30 bg-gray-100 cursor-not-allowed',
};
<IlamyCalendar
events={events}
businessHours={{
daysOfWeek: ['monday', 'tuesday', 'wednesday', 'thursday', 'friday'],
startTime: 9,
endTime: 17,
}}
classesOverride={classesOverride}
/>

Props passed to custom event form components via the renderEventForm prop. Use this interface to build your own event creation and editing forms.

EventFormProps Type Definition
interface EventFormProps {
// Whether the form should be displayed
open?: boolean;
// The event being edited (null/undefined for new events)
selectedEvent?: CalendarEvent | null;
// Callback to create a new event
onAdd?: (event: CalendarEvent) => void;
// Callback to update an existing event
onUpdate?: (event: CalendarEvent) => void;
// Callback to delete an event
onDelete?: (event: CalendarEvent) => void;
// Callback to close the form (required)
onClose: () => void;
}

Implementing a custom event form with EventFormProps:

const MyEventForm = (props: EventFormProps) => {
const { open, selectedEvent, onAdd, onUpdate, onDelete, onClose } = props;
if (!open) return null;
return (
<div className="modal">
<h2>{selectedEvent ? 'Edit Event' : 'New Event'}</h2>
{/* Your custom form UI */}
<button onClick={onClose}>Close</button>
</div>
);
};
// Usage
<IlamyCalendar
events={events}
renderEventForm={(props) => <MyEventForm {...props} />}
/>