Event recurrence
ℹ️ Important note before getting started
This plugin enables you to create recurring events according to the iCalendar specification . Please note, however, that this plugin only offers a partial implementation of the specification. There are 2 reasons for this:
- Some rules just haven’t been implemented yet. If there is a rule that you would like to see implemented, please open an issue on GitHub.
- Some iCalendar rules are supported for display purposes but cannot be updated via drag & drop. For example, events
using
BYDAYwithMONTHLYorYEARLYfrequencies are fully supported, but cannot be updated by dragging. If you need to update such events programmatically, you can disable drag & drop for those specific events or handle updates through the events service.
If you need an almost full implementation of the iCalendar spec, you can use the rrule library instead of this plugin. Here’s an example of how to do so. However, this only works if you’re merely using the calendar to display events. If you want to use interactive features like drag & drop or resizing, you cannot use the external rrule library.
Installation
npm install @schedule-x/event-recurrenceUsage
import { createEventRecurrencePlugin } from "@schedule-x/event-recurrence";
const calendar = createCalendar({
// ... other config options
plugins: [
createEventRecurrencePlugin()
],
events: [
{
id: 123,
title: 'Bi-Weekly Event Monday and Wednesday',
start: Temporal.ZonedDateTime.from('2024-02-05T14:00:00+01:00[Europe/Berlin]'),
end: Temporal.ZonedDateTime.from('2024-02-05T15:00:00+01:00[Europe/Berlin]'),
rrule: 'FREQ=WEEKLY;INTERVAL=2;BYDAY=MO,WE;UNTIL=20240229T235959'
},
{
id: 456,
title: 'Weekly Event on 4 occasions',
start: Temporal.PlainDate.from('2024-02-03'),
end: Temporal.PlainDate.from('2024-02-03'),
rrule: 'FREQ=WEEKLY;COUNT=4'
},
{
id: 789,
title: 'Daily event 5 times',
start: Temporal.ZonedDateTime.from('2024-02-05T12:00:00+01:00[Europe/Berlin]'),
end: Temporal.ZonedDateTime.from('2024-02-05T13:55:00+01:00[Europe/Berlin]'),
rrule: 'FREQ=DAILY;COUNT=5',
calendarId: 'personal',
},
{
id: 121314,
title: 'Daily event mo-fr 10 times',
start: Temporal.ZonedDateTime.from('2024-02-05T12:00:00+01:00[Europe/Berlin]'),
end: Temporal.ZonedDateTime.from('2024-02-05T13:55:00+01:00[Europe/Berlin]'),
rrule: 'FREQ=DAILY;COUNT=10;BYDAY=MO,TU,WE,TH,FR',
calendarId: 'work',
},
{
id: 141617,
title: 'Monthly event on the 7th 5 times',
start: Temporal.ZonedDateTime.from('2024-02-07T16:00:00+01:00[Europe/Berlin]'),
end: Temporal.ZonedDateTime.from('2024-02-07T17:55:00+01:00[Europe/Berlin]'),
rrule: 'FREQ=MONTHLY;COUNT=5',
},
{
id: 151618,
title: 'Monthly event on the 1st Monday 12 times',
start: Temporal.ZonedDateTime.from('2024-08-05T04:00:00+01:00[Europe/Berlin]'),
end: Temporal.ZonedDateTime.from('2024-08-05T06:00:00+01:00[Europe/Berlin]'),
rrule: 'FREQ=MONTHLY;BYDAY=1MO;COUNT=12',
},
{
rrule: 'FREQ=YEARLY;COUNT=5',
title: 'Event on the 8th of February for 5 years',
start: Temporal.ZonedDateTime.from('2024-02-08T16:00:00+01:00[Europe/Berlin]'),
end: Temporal.ZonedDateTime.from('2024-02-08T17:55:00+01:00[Europe/Berlin]'),
id: 181920
}
]
});Supported rules
| Rule | Supported values | Required |
|---|---|---|
| FREQ | DAILY, WEEKLY, MONTHLY or YEARLY | Yes |
| COUNT | Number | No |
| INTERVAL | Number | No |
| BYDAY - compatible with all frequencies | MO, TU, WE, TH, FR, SA, SU. Can optionally be prefixed with a position (e.g., 1MO for first Monday, -1FR for last Friday) but can’t be updated using drag & drop. | No |
BYMONTHDAY - compatible with MONTHLY | (Single) Positive integer 1-31. Does not support list of values, since such lists cannot be updated in a predictable way using drag & drop. | No |
| UNTIL | Floating date , for example 20240101 or date-time 20240101T120000 | No |
| WKST | MO, TU, WE, TH, FR, SA, SU | No |
exdate
You can exclude certain dates or date-times from a recurrence set using the exdate rule. The expected format is RFC5455 floating date (e.g. 20240101) or datetime (e.g. 20240101T120000). For example:
const recurringEventWithExclusions = {
id: 123,
title: 'Weekly event',
start: Temporal.ZonedDateTime.from('2024-02-05T14:00:00+01:00[Europe/Berlin]'),
end: Temporal.ZonedDateTime.from('2024-02-05T15:00:00+01:00[Europe/Berlin]'),
// will create a recurrence set of 3 events: 2024-02-05 14:00, 2024-02-26 14:00, 2024-03-04 14:00
rrule: 'FREQ=WEEKLY;COUNT=5',
exdate: [
'20240212T140000',
'20240219T140000'
]
}Events service
When using this plugin, you can no longer use the regular events service plugin. Instead, you now need to import the events service plugin from this package:
import { createEventRecurrencePlugin, createEventsServicePlugin } from "@schedule-x/event-recurrence";
const recurrencePlugin = createEventRecurrencePlugin();
const eventsServicePlugin = createEventsServicePlugin();
const calendar = createCalendar({
/* other config */
plugins: [
recurrencePlugin,
eventsServicePlugin
]
});
calendar.render(document.getElementById('calendar'));
// Add an event
eventsServicePlugin.add({
id: 1,
title: 'New event',
start: Temporal.ZonedDateTime.from('2024-02-05T14:00:00+01:00[Europe/Berlin]'),
end: Temporal.ZonedDateTime.from('2024-02-05T15:00:00+01:00[Europe/Berlin]'),
rrule: 'FREQ=WEEKLY;COUNT=3'
});
// Update
eventsServicePlugin.update({
id: 1,
title: 'New event',
start: Temporal.ZonedDateTime.from('2024-02-05T14:00:00+01:00[Europe/Berlin]'),
end: Temporal.ZonedDateTime.from('2024-02-05T15:00:00+01:00[Europe/Berlin]'),
rrule: 'FREQ=WEEKLY;COUNT=10'
})
// Get one event with id 123
const event = eventsServicePlugin.get(123);
// Get all
const events = eventsServicePlugin.getAll();
// Remove an event with id 123
eventsServicePlugin.remove(123);