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
| prop | type | description | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
id | string; optional | The id used to identify this component in Dash callbacks. | ||||||||
areEventsDraggable | boolean; optional | Allow drag-to-reschedule. Default True. | ||||||||
areEventsResizable | boolean \ | a value equal to: 'start', 'end'; optional | Allow resize (bool, or restrict to "start"/"end"). Default True. | |||||||
canDragEventsFromTheOutside | boolean; optional | Allow external events to be dragged in. Default False. | ||||||||
canDropEventsToTheOutside | boolean; optional | Allow events to be dragged out of the calendar. Default False. | ||||||||
className | string; optional | CSS class applied to the wrapping div. | ||||||||
defaultPreferences | dict; optional | Uncontrolled 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) | ||||||||
defaultView | a value equal to: 'day', 'week', 'month', 'agenda'; optional | Uncontrolled initial view. Default "week". | ||||||||
defaultVisibleDate | string; optional | Uncontrolled initial visible date (ISO string). Default today. | ||||||||
defaultVisibleResources | dict; optional | Uncontrolled initial resource visibility map. Default {} (all visible). | ||||||||
displayTimezone | string; optional | Render timezone: IANA name, or "default"/"locale"/"UTC". Default "default". | ||||||||
eventColor | a value equal to: 'red', 'pink', 'purple', 'indigo', 'blue', 'teal', 'green', 'lime', 'amber', 'orange', 'grey'; optional | Default color palette for all events (overridable). Default "teal". | ||||||||
eventCreation | dict; optional | Configures 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) | |||||||
eventDialogTopOffset | number; optional | On 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. | ||||||||
eventDialogVariant | a 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. | ||||||||
events | list of dicts; optional | The 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) |
height | number \ | string; default 600 | Height of the wrapping container (the calendar fills it). Default 600. | |||||||
lastAction | dict; optional | Convenience 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) | ||||||||
licenseKey | string; optional | MUI 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. | ||||||||
localeText | dict; optional | Override UI label strings (a partial map of translation keys). | ||||||||
mobileBreakpoint | number; default 768 | Width (px) below which the UI switches to its mobile layout. Default 768. | ||||||||
preferences | dict; optional | Controlled 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) | ||||||||
preferencesMenuConfig | dict; optional | Which 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) | |||||||
readOnly | boolean; optional | Global read-only mode (disables create / drag / resize / dialog). | ||||||||
resources | list of dicts; optional | Resources events can be assigned to (supports nested children). | ||||||||
responsiveSidePanel | boolean; default True | When True (default), the side panel starts open on wide screens and collapsed below mobileBreakpoint on first render — unless you pin isSidePanelOpen via preferences / defaultPreferences. | ||||||||
scrollToCurrentTime | boolean; default False | In 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. | ||||||||
shouldEventRequireResource | boolean; optional | Require every event to be assigned to a resource. Default False. | ||||||||
showCurrentTimeIndicator | boolean; optional | Show the current-time indicator line in time views. Default True. | ||||||||
sx | dict; optional | MUI sx styling object applied to the calendar (object form only). | ||||||||
view | a value equal to: 'day', 'week', 'month', 'agenda'; optional | Controlled active view. Also an OUTPUT. | ||||||||
views | list of a value equal to: 'day', 'week', 'month', 'agenda's; optional | Which views are offered. Default ["day","week","month","agenda"]. | ||||||||
visibleDate | string; optional | Controlled visible date (ISO string). Also an OUTPUT. | ||||||||
visibleResources | dict; optional | Controlled 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:
- /recurrence/llms.txt — LLM-friendly documentation
- /sitemap.xml
- /robots.txt