Calendars
The calendar component — a month, week or day view of date-bearing records.
A calendar places each record on the date its dateField names. The field mappings sit at the top level; only click handling and the time-grid settings live under calendarEvent and calendarInteraction.
| Path | Kind | Values | Default | Description |
|---|---|---|---|---|
dateField |
string | Date/datetime field for calendar event position | ||
endDateField |
string | End date field for multi-day calendar events | ||
defaultView |
enum | month, week, day |
Calendar display mode: month grid, week view, or day view | |
labelField |
string | Field to use as event label on calendar | ||
colorField |
string | Field whose values colour each event. A value whose option declares no colour is given one from a fixed fallback palette, hashed from the value — unlike a table’s rowColorField, which fills only from declared option colours. The hash is stable per value and distinct values may collide. | ||
maxEventsPerDay |
number | Max events visible per day cell before "+N more" |
dataSource
| Path | Kind | Values | Default | Description |
|---|---|---|---|---|
dataSource |
object | DB-table binding (DataSource) OR a system read-endpoint binding | ||
dataSource.table |
string | Table to bind to: a declared name (validated against app.tables), or a $param.<name> route reference declared by the page path | ||
dataSource.fields |
array | Specific fields to fetch from the table | ||
dataSource.fields[] |
string | One field name, spelled as the bound table declares it | ||
dataSource.mode |
enum | list, single, search |
Data fetching mode: 'list' (multiple), 'single' (one record), 'search' (interactive) | |
dataSource.filter |
array | Filter conditions applied with AND logic | ||
dataSource.filter[] |
object (truncated) | Single filter condition for data source queries | ||
dataSource.sort |
array | Sort rules applied in order | ||
dataSource.sort[] |
object (truncated) | Single sort rule for data source queries | ||
dataSource.pagination |
object | Pagination configuration for data source | ||
dataSource.pagination.pageSize |
number | Number of records per page | ||
dataSource.pagination.style |
enum | numbered, loadMore, infinite |
How pagination controls are displayed (default: numbered). infinite is accepted but not implemented and pages as numbered. |
|
dataSource.param |
string | Route parameter name for single mode (e.g., slug, id) | ||
dataSource.searchEngine |
enum | client, fts, trigram, hybrid |
Search backend for this data source (default: 'client'). Only 'client' is dispatched today; the other three validate and search as 'client' does. | |
dataSource.searchFields |
array | Fields to search across in search mode | ||
dataSource.searchFields[] |
string | One field name the search term is matched against | ||
dataSource.debounceMs |
number | Debounce delay for search input (ms) | ||
dataSource.limit |
number | Maximum number of results to return | ||
dataSource.targetId |
string | Publisher-side identifier for cross-component references — addressable by a FilterAction (targetDataSource) and by a sibling subscriber's bindTo (shared filter/period state) | ||
dataSource.bindTo |
string | ID of a publisher component whose value drives this data source (cross-component binding). By default a search-input whose query string drives the search; when sharedFilter is also set, a shared filter/period selector whose published params are merged into every request |
||
dataSource.sharedFilter |
object | Companion to bindTo: the bound publisher is a shared filter/period selector whose published params are merged into every request this data source issues. One selector can drive many sibling subscribers. Inert without bindTo. | ||
dataSource.sharedFilter.params |
array (truncated) | Request-param keys this subscriber consumes from the shared publisher's value bag (omit to merge the full bag verbatim) | ||
dataSource.refreshMode |
enum | none, poll, realtime |
Data refresh strategy for this binding (default: 'none'). 'poll' uses pollIntervalMs; 'realtime' subscribes to live change events. | |
dataSource.pollIntervalMs |
number | 30000 | Polling interval in milliseconds for refreshMode: poll (min 1000, max 300000). Defaults to 30000 when omitted, and is ignored unless refreshMode is 'poll'. | |
dataSource.system |
object | Read-endpoint binding: feed a data-bound component from a system endpoint instead of a DB table | ||
dataSource.system.endpoint |
string | Read endpoint path to fetch rows from (e.g. /api/admin/automations/runs) | ||
dataSource.system.rowsKey |
string | Key of the rows array in the response envelope (default: 'items') | ||
dataSource.system.param |
string | Route parameter substituted into the endpoint's :param placeholder. Must be declared by the host page's path. | ||
dataSource.system.idKey |
string | Key of each row's unique id (default: 'id') | ||
dataSource.system.totalKey |
string | Key of the total-count in the envelope; falls back to rows length if absent | ||
dataSource.system.query |
object | Static query params merged into every request to the endpoint | ||
dataSource.system.bindTo |
string | ID of a sibling shared filter/period selector whose published params are merged into every request to endpoint (the dynamic counterpart to the static query) | ||
dataSource.system.sharedFilter |
object (truncated) | Companion to bindTo: the shared selector params merged (dynamically) into every request to endpoint, alongside the static query. Inert without bindTo. |
autoSave
| Path | Kind | Values | Default | Description |
|---|---|---|---|---|
autoSave |
object | Configuration for automatic persistence of edits. Applies to table, form, kanban, and calendar components. | ||
autoSave.saveMode |
enum | auto, onBlur, manual |
Save trigger strategy: 'auto' (debounced), 'onBlur' (field blur), 'manual' (button). Default: 'manual'. | |
autoSave.autoSaveDebounceMs |
number | Debounce delay for auto-save in milliseconds (default: 500, min: 100) | ||
autoSave.showSaveIndicator |
boolean | Display a save status indicator (Saving... / Saved / Error). Default: true when saveMode is auto or onBlur. | ||
autoSave.saveIndicatorPosition |
enum | inline, toast, toolbar |
Where the save status indicator appears |
search
| Path | Kind | Values | Default | Description |
|---|---|---|---|---|
search |
object | Shared search bar configuration for data-bound components (table, kanban, calendar) | ||
search.enabled |
boolean | Enable search bar (default: true) | ||
search.placeholder |
string | Search input placeholder text | ||
search.debounceMs |
number | Debounce delay for search input in ms (default: 300) | ||
search.highlight |
boolean | Highlight matched search terms in results (default: false) |
calendarEvent
| Path | Kind | Values | Default | Description |
|---|---|---|---|---|
calendarEvent |
object | Configuration for how events are displayed and interacted with on the calendar | ||
calendarEvent.onEventClick |
object | Component action. Discriminated by type: auth (authentication), crud (data operations), automation (invoke workflow), filter (cross-component filtering), navigate (pure URL navigation), toast (transient notification), fetch (client-side HTTP with toast response). Open-drawer is discriminated by action: openDrawer instead of type (PG-04 quick-edit drawer pattern). |
||
calendarEvent.onEventClick.type |
enum | auth, crud, automation, filter, navigate, toast, fetch |
Which kind of action this is. It decides which of the other keys apply. | |
calendarEvent.onEventClick.method |
enum | login, signup, logout, resetPassword, setNewPassword, verifyEmail, GET, POST, PUT, PATCH, DELETE |
What the action performs: the authentication operation under type: auth, or the HTTP verb under type: fetch (default GET). |
|
calendarEvent.onEventClick.strategy |
enum | email, magicLink, oauth |
Authentication strategy to use | |
calendarEvent.onEventClick.provider |
string | OAuth provider name (e.g., google, github) | ||
calendarEvent.onEventClick.submitLabel |
string | Submit-button label for the form this action embeds. Defaults to the localized built-in label for the method or operation. Supports key translation references. | ||
calendarEvent.onEventClick.pendingLabel |
string | Custom in-flight (pending) submit-button label for the auth form. Defaults to the localized built-in pending label for the method. Supports key translation references. | ||
calendarEvent.onEventClick.fields |
array (truncated) | Per-field label/placeholder overrides for the form this action embeds. Targets fields by name. Additive — the default fields are used when omitted. | ||
calendarEvent.onEventClick.onSuccess |
object (truncated) | |||
calendarEvent.onEventClick.onError |
object (truncated) | |||
calendarEvent.onEventClick.operation |
enum | create, update, delete |
Data operation to perform | |
calendarEvent.onEventClick.table |
string | Table to perform the operation on | ||
calendarEvent.onEventClick.confirm |
boolean | If true, shows a confirmation prompt before executing the action | ||
calendarEvent.onEventClick.confirmMessage |
string | Custom confirmation message. Defaults to a generic confirmation prompt. | ||
calendarEvent.onEventClick.data |
object | Field values to apply in bulk update operations | ||
calendarEvent.onEventClick.name |
string | Automation name (must match an automation defined in app.automations) | ||
calendarEvent.onEventClick.inputData |
object | Key-value pairs passed to the automation as input. Supports $variable references. | ||
calendarEvent.onEventClick.await |
boolean | Wait for completion before triggering response (default: false = fire-and-forget) | ||
calendarEvent.onEventClick.targetDataSource |
string | ID of the data source to filter (matches dataSource.targetId) | ||
calendarEvent.onEventClick.field |
string | Field name to apply the filter to | ||
calendarEvent.onEventClick.operator |
enum | eq, neq, contains, gt, lt, gte, lte |
Comparison operator (defaults to eq) | |
calendarEvent.onEventClick.path |
string | Destination URL path (supports $record.X substitution) | ||
calendarEvent.onEventClick.message |
string | Toast notification message. Supports $variable references. | ||
calendarEvent.onEventClick.variant |
enum | success, error, warning, info |
Visual style of the toast notification | |
calendarEvent.onEventClick.duration |
number | Auto-dismiss duration in milliseconds (default: 5000) | ||
calendarEvent.onEventClick.url |
string | Target URL (any absolute path or fully-qualified URL; not prefix-restricted). e.g. /api/tables/contacts/records, /api/auth/admin/ban-user, /api/buckets/default/files/<key> | ||
calendarEvent.onEventClick.mode |
enum | fetch, navigate, download, oauth |
Dispatch mode: fetch (default, client fetch + toast), navigate (browser navigation, e.g. ?format=csv export), download (native file download), oauth (authorize → provider → callback round-trip) | |
calendarEvent.onEventClick.headers |
object | Request headers. Content-Type defaults to application/json when body is set. | ||
calendarEvent.onEventClick.body |
object | JSON request body (serialized with JSON.stringify). String values support $record.<field> and the $session.<field> token (resolved client-side from the caller session, e.g. { confirm: "$session.email" }). | ||
calendarEvent.onEventClick.filename |
string | Suggested download filename. Only meaningful when mode is "download". | ||
calendarEvent.onEventClick.redirectKey |
string | Response field holding the OAuth provider redirect URL (default "url"). Only meaningful when mode is "oauth". | ||
calendarEvent.onEventClick.callbackPath |
string | OAuth provider return path (the registered redirect_uri). Only meaningful when mode is "oauth". | ||
calendarEvent.onEventClick.responseEnvelope |
enum | sovrium, better-auth, raw |
sovrium | Response-envelope interpretation: sovrium (default), better-auth (always-200 enumeration-safe envelope at /api/auth/admin/*), raw (status-only, no body assumptions) |
calendarEvent.onEventClick.action |
enum | openDrawer |
Set to openDrawer to open a sibling drawer instead of running one of the type actions. |
|
calendarEvent.onEventClick.component |
string | ID of the drawer page-component to open (matches a sibling { type: 'drawer', id }) |
||
calendarEvent.onEventClick.props |
object (truncated) | Per-trigger overrides applied to the referenced drawer component |
calendarInteraction
| Path | Kind | Values | Default | Description |
|---|---|---|---|---|
calendarInteraction |
object | Configuration for calendar user interactions (date clicks, drag, time slots) | ||
calendarInteraction.onDateClick |
object | Component action. Discriminated by type: auth (authentication), crud (data operations), automation (invoke workflow), filter (cross-component filtering), navigate (pure URL navigation), toast (transient notification), fetch (client-side HTTP with toast response). Open-drawer is discriminated by action: openDrawer instead of type (PG-04 quick-edit drawer pattern). |
||
calendarInteraction.onDateClick.type |
enum | auth, crud, automation, filter, navigate, toast, fetch |
Which kind of action this is. It decides which of the other keys apply. | |
calendarInteraction.onDateClick.method |
enum | login, signup, logout, resetPassword, setNewPassword, verifyEmail, GET, POST, PUT, PATCH, DELETE |
What the action performs: the authentication operation under type: auth, or the HTTP verb under type: fetch (default GET). |
|
calendarInteraction.onDateClick.strategy |
enum | email, magicLink, oauth |
Authentication strategy to use | |
calendarInteraction.onDateClick.provider |
string | OAuth provider name (e.g., google, github) | ||
calendarInteraction.onDateClick.submitLabel |
string | Submit-button label for the form this action embeds. Defaults to the localized built-in label for the method or operation. Supports key translation references. | ||
calendarInteraction.onDateClick.pendingLabel |
string | Custom in-flight (pending) submit-button label for the auth form. Defaults to the localized built-in pending label for the method. Supports key translation references. | ||
calendarInteraction.onDateClick.fields |
array (truncated) | Per-field label/placeholder overrides for the form this action embeds. Targets fields by name. Additive — the default fields are used when omitted. | ||
calendarInteraction.onDateClick.onSuccess |
object (truncated) | |||
calendarInteraction.onDateClick.onError |
object (truncated) | |||
calendarInteraction.onDateClick.operation |
enum | create, update, delete |
Data operation to perform | |
calendarInteraction.onDateClick.table |
string | Table to perform the operation on | ||
calendarInteraction.onDateClick.confirm |
boolean | If true, shows a confirmation prompt before executing the action | ||
calendarInteraction.onDateClick.confirmMessage |
string | Custom confirmation message. Defaults to a generic confirmation prompt. | ||
calendarInteraction.onDateClick.data |
object | Field values to apply in bulk update operations | ||
calendarInteraction.onDateClick.name |
string | Automation name (must match an automation defined in app.automations) | ||
calendarInteraction.onDateClick.inputData |
object | Key-value pairs passed to the automation as input. Supports $variable references. | ||
calendarInteraction.onDateClick.await |
boolean | Wait for completion before triggering response (default: false = fire-and-forget) | ||
calendarInteraction.onDateClick.targetDataSource |
string | ID of the data source to filter (matches dataSource.targetId) | ||
calendarInteraction.onDateClick.field |
string | Field name to apply the filter to | ||
calendarInteraction.onDateClick.operator |
enum | eq, neq, contains, gt, lt, gte, lte |
Comparison operator (defaults to eq) | |
calendarInteraction.onDateClick.path |
string | Destination URL path (supports $record.X substitution) | ||
calendarInteraction.onDateClick.message |
string | Toast notification message. Supports $variable references. | ||
calendarInteraction.onDateClick.variant |
enum | success, error, warning, info |
Visual style of the toast notification | |
calendarInteraction.onDateClick.duration |
number | Auto-dismiss duration in milliseconds (default: 5000) | ||
calendarInteraction.onDateClick.url |
string | Target URL (any absolute path or fully-qualified URL; not prefix-restricted). e.g. /api/tables/contacts/records, /api/auth/admin/ban-user, /api/buckets/default/files/<key> | ||
calendarInteraction.onDateClick.mode |
enum | fetch, navigate, download, oauth |
Dispatch mode: fetch (default, client fetch + toast), navigate (browser navigation, e.g. ?format=csv export), download (native file download), oauth (authorize → provider → callback round-trip) | |
calendarInteraction.onDateClick.headers |
object | Request headers. Content-Type defaults to application/json when body is set. | ||
calendarInteraction.onDateClick.body |
object | JSON request body (serialized with JSON.stringify). String values support $record.<field> and the $session.<field> token (resolved client-side from the caller session, e.g. { confirm: "$session.email" }). | ||
calendarInteraction.onDateClick.filename |
string | Suggested download filename. Only meaningful when mode is "download". | ||
calendarInteraction.onDateClick.redirectKey |
string | Response field holding the OAuth provider redirect URL (default "url"). Only meaningful when mode is "oauth". | ||
calendarInteraction.onDateClick.callbackPath |
string | OAuth provider return path (the registered redirect_uri). Only meaningful when mode is "oauth". | ||
calendarInteraction.onDateClick.responseEnvelope |
enum | sovrium, better-auth, raw |
sovrium | Response-envelope interpretation: sovrium (default), better-auth (always-200 enumeration-safe envelope at /api/auth/admin/*), raw (status-only, no body assumptions) |
calendarInteraction.onDateClick.action |
enum | openDrawer |
Set to openDrawer to open a sibling drawer instead of running one of the type actions. |
|
calendarInteraction.onDateClick.component |
string | ID of the drawer page-component to open (matches a sibling { type: 'drawer', id }) |
||
calendarInteraction.onDateClick.props |
object (truncated) | Per-trigger overrides applied to the referenced drawer component | ||
calendarInteraction.timeSlotInterval |
number | Time slot interval in minutes for week/day views (default: 60) | ||
calendarInteraction.showCurrentTimeIndicator |
boolean | Show a horizontal line at the current time in week/day views |
dateField supplies each event's start and endDateField its end, for events that span time. labelField is rendered as the event's label, and defaultView is month, week or day. maxEventsPerDay caps a day cell before it collapses into a "+N more" affordance.
calendarEvent.onEventClick runs when an event is clicked and calendarInteraction.onDateClick when an empty date or slot is. calendarInteraction.timeSlotInterval is the slot length in minutes for week and day views, and showCurrentTimeIndicator draws a line at the current time.
tables:
- name: bookings
fields:
- { name: customer_name, type: single-line-text }
- { name: starts_at, type: datetime }
- { name: ends_at, type: datetime }
pages:
- name: Bookings
path: /bookings
components:
- type: calendar
dataSource: { table: bookings }
dateField: starts_at
endDateField: ends_at
labelField: customer_name
defaultView: week
calendarInteraction: { timeSlotInterval: 30, showCurrentTimeIndicator: true }A coloured event is a block, not a dot
A calendar reads colorField at the top level — the record-view colour rules are the same ones a kanban board follows, including where the hue comes from and what happens when a value declares none.
In month view a calendar renders a timed event as a small dot by default. A dot is mostly empty space, so a colour applied to it is close to invisible. An event that carries a colour therefore renders as a filled block instead; an event with no colour keeps the default dot.
Expect that as a visible layout change on any month-view calendar that sets colorField — day cells that used to hold lines of dots now hold bands of filled blocks.
Behaviour
Data Calendar
type: 'calendar'is a valid component type in page componentsdataSource.tablebinds the calendar to a tabledateFieldmaps a record's date/datetime field to calendar positionendDateFieldenables multi-day events spanning across cellsdefaultViewsets the initial view mode (month, week, day)- Navigation buttons (previous, next, today) change the visible period
labelFieldrenders the record field value as the event labelcolorFieldmaps field values to event background colors- Multi-day events span across date cells from dateField to endDateField
maxEventsPerDaylimits visible events with a "+N more" overflowonEventClicktriggers a navigation or modal action on event clickonDateClicktriggers a create action with the clicked date pre-filled- Dragging an event to a different date updates the
dateField - Week/day views show time slots based on
timeSlotInterval showCurrentTimeIndicatorrenders a line at the current time- The week and day views render rather than throwing on a slot duration
- A timed view honours the slot interval and the now-indicator
- User can complete full calendar workflow end-to-end (regression)
System read-endpoint data source
- A system-source calendar whose
dateField/labelFieldare NOT app.tables fields boots successfully (field cross-validation is skipped) - With
dataSource.system, the calendar fetchessystem.endpointand never falls through to/api/tables/:t/records - The
{ items: [...] }envelope is normalized to calendar events;totalfalls back to rows length whentotalKeyis absent dateFieldpositions andlabelFieldlabels each event against endpoint rows (config drives rendering, not a table schema)dataSource.system.querystatic params are merged into the endpoint request- A system-source calendar offers no record-write affordances (no drag-to-reschedule, no
onDateClickcreate, no event edit) - A system-source calendar offers no saved/user views and no user-preferences (read-only, endpoint-shaped)
- View switching (month/week/day) stays ON for a system source — a read-side control that re-reads the endpoint, never the records API
- The existing
dataSource.tablebinding still positions table rows on the date grid unchanged (discriminated-union regression) - A system-source calendar validates, fetches its endpoint, normalizes the envelope into events, gates writes off, keeps view switching, leaves DB intact
Last updated September 23, 2026
This documentation was written with AI, so errors or outdated content are possible. Sovrium is in beta. Contributions and corrections are welcome.