Event & Schedule Calendar
Build event or schedule calendars with event indicators, CSS-only tooltips, or full month schedule views.
Event & Schedule Calendar
The <Calendar /> component and the useDates hook support custom modifiers, CSS-only tooltips, and custom cell rendering, making it straightforward to build event and schedule calendars.
Event Indicator View
Pass custom modifiers, getDayProps to attach a dynamic data-tooltip attribute, and CSS pseudo-elements (before:content-[attr(data-tooltip)] / after:) to display event indicators and dynamic hover tooltips completely via CSS.
Live Preview: Event / Schedule Calendar
SUN
MON
TUE
WED
THU
FRI
SAT
import { useState } from 'react';
import { Calendar } from 'react-modular-datepicker';
import 'react-modular-datepicker/dist/index.css';
interface EventItem {
title: string;
time: string;
type: 'meeting' | 'webinar' | 'deadline';
}
const today = new Date();
const currentYear = today.getFullYear();
const currentMonth = today.getMonth();
const eventsData: Record<string, EventItem[]> = {
[`${currentYear}-${String(currentMonth + 1).padStart(2, '0')}-05`]: [
{ title: 'Team Sync & Standup', time: '09:00 AM', type: 'meeting' },
{ title: 'Project Kickoff', time: '02:00 PM', type: 'meeting' },
],
[`${currentYear}-${String(currentMonth + 1).padStart(2, '0')}-12`]: [
{ title: 'React Modular Datepicker Webinar', time: '11:00 AM', type: 'webinar' },
],
[`${currentYear}-${String(currentMonth + 1).padStart(2, '0')}-18`]: [
{ title: 'Q3 Product Release Deadline', time: '05:00 PM', type: 'deadline' },
],
};
export function EventScheduleExample() {
const [selectedDate, setSelectedDate] = useState<Date>(new Date(currentYear, currentMonth, 5));
const getKey = (d: Date) => `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`;
return (
<Calendar
selected={selectedDate}
onChange={(d) => setSelectedDate(d as Date)}
modifiers={{
hasEvents: (date) => !!eventsData[getKey(date)],
}}
getDayProps={(dateObj) => {
const events = eventsData[getKey(dateObj.date)];
if (events && events.length > 0) {
return {
'data-tooltip': `${events.length} event${events.length > 1 ? 's' : ''}`,
};
}
return {};
}}
classNames={{
day: {
hasEvents:
'font-bold relative after:content-["•"] after:absolute after:bottom-0.5 after:left-1/2 after:-translate-x-1/2 after:text-amber-500 ' +
'before:content-[attr(data-tooltip)] before:absolute before:bottom-full before:left-1/2 before:-translate-x-1/2 before:mb-1.5 before:hidden hover:before:block ' +
'before:px-2 before:py-1 before:bg-gray-800 before:text-white before:text-[10px] before:rounded before:whitespace-nowrap before:z-20 before:shadow-md',
},
}}
/>
);
}Full Month Schedule View
Using the headless useDates hook, you can render full-sized date cells containing event badges with titles and times.
Live Preview: Full Month Schedule View
October 2026Month View
Sun
Mon
Tue
Wed
Thu
Fri
Sat
27
28
29
30
1
2
3
4
5
9amTeam Standup
2pmProject Review
6
7
8
9
10
11
12
11amWebinar Live
13
14
15
16
17
18
5pmRelease v2.0
19
20
21
22
23
24
25
10amDesign Review
3pmSprint Retro
26
27
28
29
30
31
Full Month Schedule Code Example
import { useState } from 'react';
import { useDates } from 'react-modular-datepicker';
interface EventItem {
title: string;
time: string;
color: string;
}
const today = new Date();
const currentYear = today.getFullYear();
const currentMonth = today.getMonth();
const eventsData: Record<string, EventItem[]> = {
[`${currentYear}-${String(currentMonth + 1).padStart(2, '0')}-05`]: [
{ title: 'Team Standup', time: '9am', color: 'bg-blue-500 text-white' },
{ title: 'Project Review', time: '2pm', color: 'bg-emerald-500 text-white' },
],
[`${currentYear}-${String(currentMonth + 1).padStart(2, '0')}-12`]: [
{ title: 'Webinar Live', time: '11am', color: 'bg-purple-500 text-white' },
],
};
export function FullMonthScheduleExample() {
const [selectedDate, setSelectedDate] = useState<Date | undefined>(new Date(currentYear, currentMonth, 5));
const { calendars, getBackProps, getForwardProps, getDateProps } = useDates({
selected: selectedDate,
onChange: (d) => setSelectedDate(d as Date),
});
const calendar = calendars[0];
if (!calendar) return null;
const getKey = (d: Date) => `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`;
return (
<div className="w-full bg-white rounded-xl border p-4">
<div className="flex items-center justify-between mb-4">
<div className="flex gap-2">
<button {...getBackProps({ calendars })} className="px-3 py-1 border rounded text-xs font-semibold">
Prev
</button>
<button {...getForwardProps({ calendars })} className="px-3 py-1 border rounded text-xs font-semibold">
Next
</button>
</div>
<span className="font-bold">
{new Date(calendar.year, calendar.month).toLocaleString('default', { month: 'long', year: 'numeric' })}
</span>
</div>
<div className="grid grid-cols-7 gap-px bg-gray-200 border rounded-lg overflow-hidden">
{['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'].map((d) => (
<div key={d} className="bg-white p-2 text-center text-xs font-semibold text-gray-500">
{d}
</div>
))}
{calendar.weeks.flat().map((dateObj, idx) => {
if (!dateObj) return <div key={idx} className="bg-white min-h-[80px]" />;
const key = getKey(dateObj.date);
const events = eventsData[key] || [];
return (
<div
key={idx}
{...getDateProps({ dateObj })}
className={`bg-white min-h-[80px] p-1 flex flex-col justify-start cursor-pointer hover:bg-gray-50 ${
!dateObj.selectable ? 'opacity-40' : ''
}`}
>
<span className={`text-xs font-medium px-1.5 py-0.5 rounded-full ${dateObj.today ? 'bg-amber-500 text-white font-bold' : ''}`}>
{dateObj.date.getDate()}
</span>
<div className="mt-1 space-y-1">
{events.map((ev, i) => (
<div key={i} className={`px-1.5 py-0.5 rounded text-[10px] font-medium truncate ${ev.color}`}>
<span className="mr-1 opacity-80">{ev.time}</span>
<span>{ev.title}</span>
</div>
))}
</div>
</div>
);
})}
</div>
</div>
);
}