Skip to main content

โšก Calendar Component - Quick Reference

Cheatsheet untuk penggunaan cepat Calendar Component.


๐Ÿ“ฆ Installationโ€‹

npm install react-big-calendar date-fns dayjs @tanstack/react-query

๐Ÿš€ Basic Usageโ€‹

import { GenericOptimizedCalendar } from '@/components/Calendar/GenericCalendar';

<GenericOptimizedCalendar
onFetchEvents={async (start, end) => {
const res = await fetch(`/api/events?start=${start}&end=${end}`);
return res.json();
}}
/>

๐Ÿ“‹ Common Propsโ€‹

PropTypeExample
onFetchEventsFunctionasync (start, end) => fetch(...)
defaultViewString'month' | 'week' | 'day' | 'agenda'
timeWindowObject/Function{ start: '08:00', end: '17:00' }
workingDaysArray[1, 2, 3, 4, 5] (Mon-Fri)
preventOverlapBooleantrue
allowBackDateBooleanfalse (disable past dates)
getEventColorFunction(e) => e.color
isEventDisabledFunction(e) => e.isPast

๐ŸŽจ Event Object Structureโ€‹

{
id: string; // Required
title: string; // Required
start: Date | string; // Required
end: Date | string; // Required
color?: string; // Optional
desc?: string; // Optional
disabled?: boolean; // Optional
allDay?: boolean; // Optional
}

๐Ÿ”ง Common Patternsโ€‹

Fetch dari APIโ€‹

const fetchEvents = async (start: Date, end: Date) => {
const res = await axios.get('/api/events', {
params: {
start: start.toISOString(),
end: end.toISOString()
}
});
return res.data;
};

Working Hours (08:00 - 17:00)โ€‹

<GenericOptimizedCalendar
onFetchEvents={fetchEvents}
timeWindow={{ start: '08:00', end: '17:00' }}
restrictVisibleHours={true}
/>

Working Days (Senin - Jumat)โ€‹

<GenericOptimizedCalendar
onFetchEvents={fetchEvents}
workingDays={[1, 2, 3, 4, 5]}
/>

Prevent Overlapโ€‹

<GenericOptimizedCalendar
onFetchEvents={fetchEvents}
preventOverlap={true}
onSelectSlotDenied={(reason) => {
if (reason === 'overlap') {
alert('Slot sudah terisi!');
}
}}
/>

Custom Colorsโ€‹

<GenericOptimizedCalendar
onFetchEvents={fetchEvents}
getEventColor={(event) => {
if (event.status === 'approved') return '#4caf50';
if (event.status === 'pending') return '#ff9800';
return '#f44336';
}}
/>

Disable Past Eventsโ€‹

<GenericOptimizedCalendar
onFetchEvents={fetchEvents}
allowBackDate={false}
isEventDisabled={(event) => {
return dayjs(event.start).isBefore(dayjs(), 'day');
}}
/>

Handle Event Clickโ€‹

<GenericOptimizedCalendar
onFetchEvents={fetchEvents}
onSelectEventSimple={(event) => {
console.log('Clicked:', event);
// Open modal/detail
}}
/>

Handle Slot Clickโ€‹

<GenericOptimizedCalendar
onFetchEvents={fetchEvents}
onSelectSlot={(slotInfo) => {
console.log('Selected:', slotInfo.start, slotInfo.end);
// Open form to create event
}}
/>

With Ref (Manual Refresh)โ€‹

const calendarRef = useRef<GenericCalendarRef>(null);

const handleRefresh = () => {
calendarRef.current?.refresh();
};

<GenericOptimizedCalendar
ref={calendarRef}
onFetchEvents={fetchEvents}
/>

Dynamic Time Windowโ€‹

const getTimeWindow = (date: Date) => {
const isWeekend = date.getDay() === 0 || date.getDay() === 6;
return isWeekend
? { start: '09:00', end: '14:00' }
: { start: '08:00', end: '17:00' };
};

<GenericOptimizedCalendar
onFetchEvents={fetchEvents}
timeWindow={getTimeWindow}
/>

With Query Paramsโ€‹

<GenericOptimizedCalendar
onFetchEvents={fetchEvents}
queryParams={{
userId: '123',
status: 'active',
category: 'meeting'
}}
/>

Custom Loading Stateโ€‹

<GenericOptimizedCalendar
onFetchEvents={fetchEvents}
renderLoading={
<div className="loading">
<Spinner />
<p>Loading events...</p>
</div>
}
/>

Responsive (Mobile)โ€‹

const isMobile = useMediaQuery('(max-width: 768px)');

<GenericOptimizedCalendar
onFetchEvents={fetchEvents}
defaultView={isMobile ? 'day' : 'month'}
/>

๐ŸŽฏ Event Handlersโ€‹

onSelectSlotโ€‹

Triggered saat user select/click slot kosong:

onSelectSlot={(slotInfo) => {
// slotInfo: { start: Date, end: Date, action: string, ... }
console.log(slotInfo.start, slotInfo.end);
}}

onSelectEventโ€‹

Triggered saat user click event (RBC default):

onSelectEvent={(event, e) => {
// event: your event object
// e: SyntheticEvent
console.log(event);
}}

onSelectEventSimpleโ€‹

Simplified version (1 parameter):

onSelectEventSimple={(event) => {
console.log(event);
}}

onSelectSlotDeniedโ€‹

Triggered saat slot denied (restrictions):

onSelectSlotDenied={(reason) => {
// reason: 'outside-working-days' | 'outside-time-window' |
// 'outside-global-bounds' | 'overlap'
console.log(reason);
}}

๐ŸŽจ Stylingโ€‹

Override Default Stylesโ€‹

/* Custom event */
.rbc-event {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-radius: 8px;
border: none;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
}

.rbc-event:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
}

/* Today highlight */
.rbc-today {
background-color: rgba(33, 150, 243, 0.08);
}

/* Selected slot */
.rbc-selected-cell {
background-color: rgba(33, 150, 243, 0.15);
}

/* Disabled slot */
.disabledSlot {
background: repeating-linear-gradient(
45deg,
#f5f5f5,
#f5f5f5 10px,
#e0e0e0 10px,
#e0e0e0 20px
);
cursor: not-allowed;
}

Custom Slot Styleโ€‹

<GenericOptimizedCalendar
onFetchEvents={fetchEvents}
disabledSlotStyle={{
backgroundColor: '#ffe6e6',
border: '1px dashed #ff0000',
opacity: 0.5,
}}
/>

๐Ÿ”ง React Query Configurationโ€‹

Stale Timeโ€‹

<GenericOptimizedCalendar
onFetchEvents={fetchEvents}
staleTimeMs={5 * 60 * 1000} // 5 minutes
/>

Custom Query Optionsโ€‹

<GenericOptimizedCalendar
onFetchEvents={fetchEvents}
queryOptions={{
cacheTime: 10 * 60 * 1000, // 10 minutes
refetchOnMount: false,
refetchOnWindowFocus: false,
retry: 3,
retryDelay: 1000,
}}
/>

๐ŸŒ Localizationโ€‹

Default (Bahasa Indonesia)โ€‹

Built-in, no config needed.


Englishโ€‹

import { enUS } from 'date-fns/locale';
import { dateFnsLocalizer } from 'react-big-calendar';

const enLocalizer = dateFnsLocalizer({
format: (date, formatStr) =>
dfFormat(date, formatStr, { locale: enUS }),
// ... other config
});

<GenericOptimizedCalendar
localizer={enLocalizer}
culture="en-US"
onFetchEvents={fetchEvents}
/>

๐Ÿ› Troubleshootingโ€‹

Calendar tidak tampilโ€‹

// โŒ Wrong
<GenericOptimizedCalendar onFetchEvents={fetchEvents} />

// โœ… Correct - add explicit height
<div style={{ height: '600px' }}>
<GenericOptimizedCalendar onFetchEvents={fetchEvents} />
</div>

Events tidak munculโ€‹

// Pastikan return array dengan format correct
const fetchEvents = async (start, end) => {
return [
{
id: '1', // โœ… Required
title: 'Meeting', // โœ… Required
start: new Date(), // โœ… Required (Date or ISO string)
end: new Date(), // โœ… Required
}
];
};

Timezone issuesโ€‹

// API return: '2025-12-05T09:00:00Z'
// Component auto-convert ke local time
// Jika ingin keep UTC, handle manually

๐Ÿ“ฆ Dependencies Versionโ€‹

Recommended versions:

{
"react-big-calendar": "^1.8.5",
"date-fns": "^3.0.0",
"dayjs": "^1.11.10",
"@tanstack/react-query": "^5.0.0",
"react": "^18.2.0"
}


๐Ÿ’ก Tipsโ€‹

  1. Always set explicit height - Calendar requires container height
  2. Use React Query cache - Set appropriate staleTime
  3. Validate date inputs - Check bounds before create/update
  4. Handle errors gracefully - Don't let calendar crash
  5. Test on mobile - Use responsive props
  6. Memoize callbacks - Optimize re-renders
  7. Use TypeScript - Better type safety
  8. Monitor performance - Use React DevTools Profiler

๐Ÿ“ฑ Mobile Quick Setupโ€‹

const isMobile = useMediaQuery('(max-width: 768px)');

<div style={{
height: isMobile ? 'calc(100vh - 60px)' : '600px',
padding: isMobile ? '8px' : '20px'
}}>
<GenericOptimizedCalendar
onFetchEvents={fetchEvents}
defaultView={isMobile ? 'day' : 'month'}
/>
</div>

๐Ÿงช Quick Testโ€‹

const mockFetchEvents = async () => {
return [
{
id: '1',
title: 'Test Event',
start: new Date(),
end: new Date(Date.now() + 3600000), // +1 hour
color: '#3174ad',
}
];
};

<GenericOptimizedCalendar onFetchEvents={mockFetchEvents} />

โšก Performance Tipsโ€‹

// 1. Memoize functions
const getEventColor = useMemo(() => (e) => e.color, []);
const isEventDisabled = useMemo(() => (e) => e.disabled, []);

// 2. Optimize query
<GenericOptimizedCalendar
staleTimeMs={5 * 60 * 1000}
queryOptions={{ refetchOnWindowFocus: false }}
/>

// 3. Lazy load
const Calendar = lazy(() => import('./Calendar'));
<Suspense fallback={<Loading />}>
<Calendar />
</Suspense>

๐Ÿ“ TypeScript Typesโ€‹

import { GenericCalendarRef } from '@/components/Calendar/GenericCalendar';
import { CalendarEvent } from '@/components/Calendar/types';
import { SlotInfo, View } from 'react-big-calendar';

// Ref
const calendarRef = useRef<GenericCalendarRef>(null);

// Event
interface MyEvent extends CalendarEvent {
customField: string;
}

// Slot
const handleSelectSlot = (slotInfo: SlotInfo) => {
const { start, end, action } = slotInfo;
};

Print this page untuk quick reference! ๐Ÿ–จ๏ธ

Last updated: December 5, 2025