Properly render both recurring and one-off events in react-big-calendar with date constraints. Use when: (1) One-off events appear every week instead of just their specific date, (2) Recurring events don't respect effectiveFrom/effectiveUntil date ranges, (3) Calendar only shows current week instead of entire view range, (4) Need to generate events dynamically based on recurrence pattern and view range. Solves event generation for calendars with mixed recurring/one-off availability blocks.
Instalar com Codex ou Claude Copie este prompt, cole no Codex, Claude ou outro assistente e deixe que ele revise a página da skill e instale para você.
Um comando direto ignora o prompt de revisão. Verifique a origem antes de executá-lo.
Instruções da origem · Visualização somente leitura
name
react-big-calendar-recurring-oneoff-events
description
Properly render both recurring and one-off events in react-big-calendar with date constraints. Use when: (1) One-off events appear every week instead of just their specific date, (2) Recurring events don't respect effectiveFrom/effectiveUntil date ranges, (3) Calendar only shows current week instead of entire view range, (4) Need to generate events dynamically based on recurrence pattern and view range. Solves event generation for calendars with mixed recurring/one-off availability blocks.
author
Claude Code
version
1.0.0
date
2026-02-06T00:00:00.000Z
react-big-calendar: Recurring vs One-Off Event Rendering
Problem
react-big-calendar requires pre-generated event objects with specific start and end Date instances. When your data includes both recurring events (e.g., "every Monday 9-5") and one-off events (e.g., "January 15, 2026 only"), a naive "anchor to current week" approach causes one-off events to appear every week instead of just their specific date.
Additionally, recurring events may have date constraints (effectiveFrom/effectiveUntil) that aren't respected, causing them to appear before they start or after they end.
Context / Trigger Conditions
Use this skill when:
One-off events repeat incorrectly: An event with effectiveFrom: 2026-02-15 and effectiveUntil: 2026-02-15 appears on Feb 15, Feb 22, Feb 29, etc. (every week)
Recurring events ignore date ranges: A recurring Monday event with effectiveFrom: 2026-03-01 appears on Mondays in February
View range issues: Calendar only shows events for the current week, even in month view
Data structure: Your backend stores events with:
dayOfWeek (0-6, Sunday-Saturday)
startTime/endTime (time of day)
effectiveFrom (start date)
effectiveUntil (end date, or null for ongoing recurring)
Error symptoms:
Events appearing on wrong dates
One-off events duplicating every week
Empty calendar in month view despite data existing
Recurring events appearing before/after their effective range
Solution
Step 1: Calculate View Range
First, determine what date range the calendar is currently displaying based on view type:
// Recurring if no end date (or end date is far in future)const isRecurring = !event.effectiveUntil;
// Convert to start of day for date comparisonsconst effectiveStart = startOfDay(event.effectiveFrom);
const effectiveEnd = event.effectiveUntil
? startOfDay(event.effectiveUntil)
: newDate(9999, 11, 31); // Far future
Step 3: Generate Events for Recurring Blocks
For recurring events, generate one event per occurrence within the view range:
import { addDays, isBefore, isAfter, isSameDay, isWithinInterval, getDay } from"date-fns";
if (isRecurring) {
let currentWeekStart = startOfWeek(viewStart);
const viewEndWeek = endOfWeek(viewEnd);
while (isBefore(currentWeekStart, viewEndWeek) || isSameDay(currentWeekStart, viewEndWeek)) {
// Calculate the date for this week's dayOfWeek occurrenceconst dayOffset = (event.dayOfWeek - getDay(currentWeekStart) + 7) % 7;
const eventDate = addDays(currentWeekStart, dayOffset);
// Check if this occurrence is within BOTH view range AND effective rangeconst isInViewRange = isWithinInterval(eventDate, { start: viewStart, end: viewEnd });
const isAfterStart = !isBefore(eventDate, effectiveStart);
const isBeforeEnd = !isAfter(eventDate, effectiveEnd);
if (isInViewRange && isAfterStart && isBeforeEnd) {
const startTime = newDate(eventDate);
startTime.setHours(event.startTime.getHours(), event.startTime.getMinutes());
const endTime = newDate(eventDate);
endTime.setHours(event.endTime.getHours(), event.endTime.getMinutes());
calendarEvents.push({
id: `${event.id}-${format(eventDate, "yyyy-MM-dd")}`, // Composite IDtitle: "Available",
start: startTime,
end: endTime,
resource: {
type: "availability",
metadata: { isRecurring: true },
originalData: event,
},
});
}
currentWeekStart = addDays(currentWeekStart, 7); // Next week
}
}
Key points:
Use composite ID${event.id}-${date} to distinguish weekly occurrences
Filter by both view range and effective date range
Loop through weeks, not days (more efficient)
Step 4: Generate Events for One-Off Blocks
For one-off events, generate a single event on the specific date:
else {
// One-off availability: generate single event on effectiveFrom dateconst eventDate = effectiveStart;
// Only show if within view rangeif (isWithinInterval(eventDate, { start: viewStart, end: viewEnd })) {
const startTime = newDate(eventDate);
startTime.setHours(event.startTime.getHours(), event.startTime.getMinutes());
const endTime = newDate(eventDate);
endTime.setHours(event.endTime.getHours(), event.endTime.getMinutes());
calendarEvents.push({
id: event.id, // Original ID (no composite)title: "Available",
start: startTime,
end: endTime,
resource: {
type: "availability",
metadata: { isRecurring: false },
originalData: event,
},
});
}
}
Key points:
Use original ID (no date suffix needed)
Only generate if effectiveFrom is within view range
Mark as isRecurring: false for visual distinction
Step 5: Update useMemo Dependencies
Include view in the dependency array so events regenerate when view changes:
// One-off block: Feb 15, 2026 onlyconst oneOff = {
id: "1",
dayOfWeek: 1, // MondaystartTime: newDate("2026-01-01T09:00:00"),
endTime: newDate("2026-01-01T17:00:00"),
effectiveFrom: newDate("2026-02-15"),
effectiveUntil: newDate("2026-02-15"),
};
// Should appear: Only on Feb 15, 2026// Should NOT appear: Feb 8, Feb 22, or any other Monday// Recurring block: Every Monday starting March 1const recurring = {
id: "2",
dayOfWeek: 1, // MondaystartTime: newDate("2026-01-01T09:00:00"),
endTime: newDate("2026-01-01T17:00:00"),
effectiveFrom: newDate("2026-03-01"),
effectiveUntil: null, // No end date
};
// Should appear: March 2, 9, 16, 23, 30... (all Mondays from March 1 onward)// Should NOT appear: Feb 23 or earlier Mondays
Large recurring ranges: If a recurring event spans years and you're showing a month view, the loop might generate hundreds of events. Consider caching or pagination.
Memoization: Always use useMemo with correct dependencies to avoid regenerating events on every render.
Edge Cases
Cross-day events: If endTime < startTime (e.g., 11 PM - 1 AM), you may need to add 1 day to the end date
Timezone handling: If startTime/endTime are stored in a specific timezone, ensure proper conversion
Daylight Saving Time: date-fns handles DST correctly, but be aware of edge cases around DST transitions
Alternative Approaches
Option 1: Backend generates events - Move this logic to the server and return pre-generated events for the requested date range. Reduces client-side complexity but increases API payload.
Option 2: Use RRULE - For complex recurrence patterns (every other Tuesday, last Friday of month), consider using the rrule library with react-big-calendar's built-in support.
Visual Distinction
Mark recurring events visually to help users distinguish them: