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.


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.

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:

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).

# 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:

{"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:

"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.

# 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.

EventCalendarPremium props

proptypedescription
idstring; optionalThe id used to identify this component in Dash callbacks.
areEventsDraggableboolean; optionalAllow drag-to-reschedule. Default True.
areEventsResizableboolean \a value equal to: 'start', 'end'; optionalAllow resize (bool, or restrict to "start"/"end"). Default True.
canDragEventsFromTheOutsideboolean; optionalAllow external events to be dragged in. Default False.
canDropEventsToTheOutsideboolean; optionalAllow events to be dragged out of the calendar. Default False.
classNamestring; optionalCSS class applied to the wrapping div.
defaultPreferencesdict; optionalUncontrolled initial preferences. defaultPreferences is a dict with keys: - ampm (boolean; optional) - weekStartsOn (a value equal to: 0, 1, 2, 3, 4, 5, 6; optional) - showWeekends (boolean; optional) - showWeekNumber (boolean; optional) - isSidePanelOpen (boolean; optional) - showEmptyDaysInAgenda (boolean; optional)
defaultViewa value equal to: 'day', 'week', 'month', 'agenda'; optionalUncontrolled initial view. Default "week".
defaultVisibleDatestring; optionalUncontrolled initial visible date (ISO string). Default today.
defaultVisibleResourcesdict; optionalUncontrolled initial resource visibility map. Default {} (all visible).
displayTimezonestring; optionalRender timezone: IANA name, or "default"/"locale"/"UTC". Default "default".
eventColora value equal to: 'red', 'pink', 'purple', 'indigo', 'blue', 'teal', 'green', 'lime', 'amber', 'orange', 'grey'; optionalDefault color palette for all events (overridable). Default "teal".
eventCreationdict; optionalConfigures event creation. False disables it; True enables defaults; an object sets {interaction, duration (minutes)}. eventCreation is a boolean \dict with keys: - interaction (a value equal to: 'click', 'double-click'; optional) - duration (number; optional)
eventDialogTopOffsetnumber; optionalOn desktop, inset the event drawer this many px from the top — e.g. set it to your fixed app header's height so the drawer lines up with a sidebar instead of covering the header. Default 0.
eventDialogVarianta value equal to: 'drawer', 'dialog'; default 'drawer'How the event editor is presented. "drawer" (default) restyles the built-in dialog into a responsive drawer — right-anchored on desktop, an 80%-height bottom sheet on mobile (below mobileBreakpoint), with a scrollable body and pinned header/actions. "dialog" keeps the library's default floating, draggable dialog.
eventslist of dicts; optionalThe events to render. Each event is a dict with at least id, title, start, end (ISO strings). Premium events may also carry rrule (recurrence) and exDates. INPUT + OUTPUT (round-trips on every change). events is a list of dicts with keys: - id (string \number; required) - title (string; required) - start (string; required) - end (string; required) - description (string; optional) - timezone (string; optional) - resource (string \number; optional) - rrule (string \dict; optional): Recurrence rule — RFC-5545 RRULE string ("FREQ=WEEKLY;INTERVAL=2;BYDAY=TH") or an object {freq:"DAILY\WEEKLY\MONTHLY\YEARLY", interval, byDay, byMonthDay, byMonth, count, until}. - exDates (list of strings; optional): Exception dates (ISO strings) excluded from the recurrence. - allDay (boolean; optional) - readOnly (boolean; optional) - color (a value equal to: 'red', 'pink', 'purple', 'indigo', 'blue', 'teal', 'green', 'lime', 'amber', 'orange', 'grey'; optional) - draggable (boolean; optional) - resizable (boolean \a value equal to: 'start', 'end'; optional) - className (string; optional) - extractedFromId (string \number; optional)
heightnumber \string; default 600Height of the wrapping container (the calendar fills it). Default 600.
lastActiondict; optionalConvenience OUTPUT describing the most recent change to events: {type, event, event_timestamp}. lastAction is a dict with keys: - type (string; optional) - event (dict; optional) - event_timestamp (number; optional)
licenseKeystring; optionalMUI X Premium license key. Set once to remove the watermark and unlock Premium features (recurrence). Read from an environment variable on the server and pass it in.
localeTextdict; optionalOverride UI label strings (a partial map of translation keys).
mobileBreakpointnumber; default 768Width (px) below which the UI switches to its mobile layout. Default 768.
preferencesdict; optionalControlled user preferences. Also an OUTPUT. preferences is a dict with keys: - ampm (boolean; optional) - weekStartsOn (a value equal to: 0, 1, 2, 3, 4, 5, 6; optional) - showWeekends (boolean; optional) - showWeekNumber (boolean; optional) - isSidePanelOpen (boolean; optional) - showEmptyDaysInAgenda (boolean; optional)
preferencesMenuConfigdict; optionalWhich items appear in the preferences menu, or False to hide it. preferencesMenuConfig is a a value equal to: false \dict with keys: - toggleWeekendVisibility (boolean; optional) - toggleWeekNumberVisibility (boolean; optional) - toggleAmpm (boolean; optional) - toggleEmptyDaysInAgenda (boolean; optional) - toggleWeekStartsOn (boolean; optional)
readOnlyboolean; optionalGlobal read-only mode (disables create / drag / resize / dialog).
resourceslist of dicts; optionalResources events can be assigned to (supports nested children).
responsiveSidePanelboolean; default TrueWhen True (default), the side panel starts open on wide screens and collapsed below mobileBreakpoint on first render — unless you pin isSidePanelOpen via preferences / defaultPreferences.
scrollToCurrentTimeboolean; default FalseIn the day / week views, scroll the time grid on first render (and on view change) so the current-time indicator is centered in view. Pairs with showCurrentTimeIndicator. Default False.
shouldEventRequireResourceboolean; optionalRequire every event to be assigned to a resource. Default False.
showCurrentTimeIndicatorboolean; optionalShow the current-time indicator line in time views. Default True.
sxdict; optionalMUI sx styling object applied to the calendar (object form only).
viewa value equal to: 'day', 'week', 'month', 'agenda'; optionalControlled active view. Also an OUTPUT.
viewslist of a value equal to: 'day', 'week', 'month', 'agenda's; optionalWhich views are offered. Default ["day","week","month","agenda"].
visibleDatestring; optionalControlled visible date (ISO string). Also an OUTPUT.
visibleResourcesdict; optionalControlled resource visibility map {resourceId: bool}. Also an OUTPUT.

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: