Recurrence

Build recurring events in dash-mui-scheduler with EventCalendarPremium using RRULE strings, RRULE objects and exception dates.

Recurrence

Build recurring events in dash-mui-scheduler with EventCalendarPremium using RRULE strings, RRULE objects and exception dates.

---

.. llms_copy::Recurrence

.. toc::

Recurrence (Premium)

Recurring events are a Premium feature. Use dms.EventCalendarPremium instead of dms.EventCalendar — it is identical to the Community calendar but adds a recurrence engine, a Recurrence tab in the edit dialog, and two new per-event keys: rrule and exDates.

.. admonition::Premium :color: yellow

EventCalendarPremium requires a MUI X Premium license key. Pass it via licenseKey=os.environ.get("MUI_X_LICENSE_KEY", ""). Without a valid key the calendar still renders and is fully interactive, but a MUI watermark is shown over it. That is expected — supply a real key to remove it.

The data boundary is unchanged from the Community calendar. events is a list of plain dicts and is both input and output: the component writes the full array back on every create, move, resize or delete. Dates are ISO strings (for example "2024-01-15T10:00:00" for wall time, or a trailing Z for UTC), never Python datetime objects. Recurring series are stored as a *single* event dict carrying an rrule; the calendar expands it into occurrences for display only.

Recurrence as an RRULE string

The simplest form sets event["rrule"] to an RFC-5545 RRULE string. The event below repeats every week on Monday, Wednesday and Friday:

``text FREQ=WEEKLY;INTERVAL=1;BYDAY=MO,WE,FR ``

FREQ is one of DAILY, WEEKLY, MONTHLY, YEARLY. BYDAY codes are MO TU WE TH FR SA SU (for monthly rules you may prefix an ordinal, e.g. 2TU = second Tuesday, -1FR = last Friday). You can also add COUNT, UNTIL (an ISO string), BYMONTHDAY (1–31) and BYMONTH (1–12).

.. exec::docs.recurrence.recurrence_string

```python

File: docs/recurrence/recurrence_string.py

import os

from dash import Input, Output, callback import dash_mantine_components as dmc import dash_mui_scheduler as dms

Premium events may carry an rrule. The RRULE string form follows RFC-5545:

this event recurs every week on Monday, Wednesday and Friday at 10:00.

events = [ { "id": "standup", "title": "Team Standup", "start": "2024-01-15T10:00:00", "end": "2024-01-15T10:30:00", "color": "blue", "rrule": "FREQ=WEEKLY;INTERVAL=1;BYDAY=MO,WE,FR", }, { "id": "review", "title": "Sprint Review", "start": "2024-01-19T15:00:00", "end": "2024-01-19T16:00:00", "color": "purple", }, ]

component = dmc.Stack( [ dms.EventCalendarPremium( id="recurrence-string-cal", licenseKey=os.environ.get("MUI_X_LICENSE_KEY", ""), events=events, defaultView="week", defaultVisibleDate="2024-01-15", height=600, ), dmc.Text("Last action:", fw=600, size="sm"), dmc.Code(id="recurrence-string-action", block=True), ], gap="sm", )

@callback( Output("recurrence-string-action", "children"), Input("recurrence-string-cal", "lastAction"), ) def show_action(last_action): if not last_action: return "No action yet — drag, create, resize or delete an occurrence." return f"{last_action.get('type')} @ {last_action.get('event_timestamp')}" ```

Recurrence as an object + exception dates

Instead of a string, rrule may be an object. This is convenient when you are building the rule programmatically:

``python {"freq": "WEEKLY", "interval": 1, "byDay": ["MO", "WE", "FR"], "count": 10} ``

The keys mirror the RRULE parts: freq, interval, byDay, byMonthDay, byMonth, count and until. To remove individual occurrences from a series without breaking the rule, add exDates — a list of ISO strings naming the start times to skip:

``python "exDates": ["2024-01-17T08:00:00", "2024-01-19T08:00:00"] ``

The example below recurs every weekday for ten occurrences, with two dates excluded. Note that even though the calendar shows many occurrences, the events output still contains a single definition dict.

.. exec::docs.recurrence.recurrence_object

```python

File: docs/recurrence/recurrence_object.py

import os

from dash import Input, Output, callback import dash_mantine_components as dmc import dash_mui_scheduler as dms

rrule may also be an object instead of a string. Here the event recurs

every weekday morning for a total of 10 occurrences, but two specific

dates are removed from the series via exDates (ISO strings).

events = [ { "id": "yoga", "title": "Morning Yoga", "start": "2024-01-15T08:00:00", "end": "2024-01-15T08:45:00", "color": "green", "rrule": { "freq": "WEEKLY", "interval": 1, "byDay": ["MO", "TU", "WE", "TH", "FR"], "count": 10, }, "exDates": [ "2024-01-17T08:00:00", "2024-01-19T08:00:00", ], }, ]

component = dmc.Stack( [ dms.EventCalendarPremium( id="recurrence-object-cal", licenseKey=os.environ.get("MUI_X_LICENSE_KEY", ""), events=events, defaultView="week", defaultVisibleDate="2024-01-15", height=600, ), dmc.Text("Occurrence count in events output:", fw=600, size="sm"), dmc.Code(id="recurrence-object-count", block=True), ], gap="sm", )

@callback( Output("recurrence-object-count", "children"), Input("recurrence-object-cal", "events"), ) def show_count(events_out): # The component writes the full event array back on every edit. The # recurring definition stays a single dict with rrule/exDates; # the UI expands it into occurrences for display only. return f"{len(events_out or [])} stored event definition(s)" ```

Reading edits back

As with the Community calendar, you do not need a callback for the calendar to be interactive — drags, creates and deletes round-trip through Dash on their own. Add a callback only to *display* outputs. The lastAction output is {type, event, event_timestamp}, where type is one of create, update, delete, move, resize or change. The first example above wires lastAction into a code block so you can watch edits as they happen.

EventCalendarPremium props

EventCalendarPremium accepts every EventCalendar prop plus licenseKey, and its events may include rrule and exDates.

.. kwargs::dash_mui_scheduler.EventCalendarPremium

---

*Source: /recurrence*

Note for AI agents: This is the static, prerendered view of an interactive Dash application served because we detected a non-JS user agent. Full prose docs: