Skip to main content
View as Markdown

Galleries

The gallery component — a responsive card grid over records, in a uniform grid, a masonry wall or a carousel.

A gallery draws one card per record. It binds through the shared dataSource module and has no pagination key of its own: filtering, sorting and paging all belong to the binding.

Path Kind Values Default Description
layout enum grid, masonry, carousel Gallery layout mode: grid | masonry | carousel. carousel lays the same cards on one horizontal track paged by its own controls, so a set too wide to grid is still walkable.
emptyMessage string Message displayed when no records match the data source query

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

gridColumns

Path Kind Values Default Description
gridColumns object Responsive column counts per breakpoint for the gallery grid layout
gridColumns.mobile number Number of columns on mobile (default: 1)
gridColumns.sm number Number of columns on small screens (>= 640px)
gridColumns.md number Number of columns on medium screens (>= 768px)
gridColumns.lg number Number of columns on large screens (>= 1024px)
gridColumns.xl number Number of columns on extra-large screens (>= 1280px)

galleryCard

Path Kind Values Default Description
galleryCard object Template configuration for how records render as gallery cards
galleryCard.coverImage string Image URL or $record.* variable for card cover image
galleryCard.aspectRatio string Cover image aspect ratio (e.g. 4:3, 16:9, 1:1)
galleryCard.children array Child component definitions for the card body
galleryCard.children[] object One child component definition, rendered inside the card
galleryCard.onClick 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).
galleryCard.onClick.type enum auth, crud, automation, filter, navigate, toast, fetch Which kind of action this is. It decides which of the other keys apply.
galleryCard.onClick.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).
galleryCard.onClick.strategy enum email, magicLink, oauth Authentication strategy to use
galleryCard.onClick.provider string OAuth provider name (e.g., google, github)
galleryCard.onClick.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.
galleryCard.onClick.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.
galleryCard.onClick.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.
galleryCard.onClick.onSuccess object (truncated)
galleryCard.onClick.onError object (truncated)
galleryCard.onClick.operation enum create, update, delete Data operation to perform
galleryCard.onClick.table string Table to perform the operation on
galleryCard.onClick.confirm boolean If true, shows a confirmation prompt before executing the action
galleryCard.onClick.confirmMessage string Custom confirmation message. Defaults to a generic confirmation prompt.
galleryCard.onClick.data object Field values to apply in bulk update operations
galleryCard.onClick.name string Automation name (must match an automation defined in app.automations)
galleryCard.onClick.inputData object Key-value pairs passed to the automation as input. Supports $variable references.
galleryCard.onClick.await boolean Wait for completion before triggering response (default: false = fire-and-forget)
galleryCard.onClick.targetDataSource string ID of the data source to filter (matches dataSource.targetId)
galleryCard.onClick.field string Field name to apply the filter to
galleryCard.onClick.operator enum eq, neq, contains, gt, lt, gte, lte Comparison operator (defaults to eq)
galleryCard.onClick.path string Destination URL path (supports $record.X substitution)
galleryCard.onClick.message string Toast notification message. Supports $variable references.
galleryCard.onClick.variant enum success, error, warning, info Visual style of the toast notification
galleryCard.onClick.duration number Auto-dismiss duration in milliseconds (default: 5000)
galleryCard.onClick.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>
galleryCard.onClick.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)
galleryCard.onClick.headers object Request headers. Content-Type defaults to application/json when body is set.
galleryCard.onClick.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" }).
galleryCard.onClick.filename string Suggested download filename. Only meaningful when mode is "download".
galleryCard.onClick.redirectKey string Response field holding the OAuth provider redirect URL (default "url"). Only meaningful when mode is "oauth".
galleryCard.onClick.callbackPath string OAuth provider return path (the registered redirect_uri). Only meaningful when mode is "oauth".
galleryCard.onClick.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)
galleryCard.onClick.action enum openDrawer Set to openDrawer to open a sibling drawer instead of running one of the type actions.
galleryCard.onClick.component string ID of the drawer page-component to open (matches a sibling { type: 'drawer', id })
galleryCard.onClick.props object (truncated) Per-trigger overrides applied to the referenced drawer component
galleryCard.hoverOverlay object Overlay content displayed on card hover
galleryCard.hoverOverlay.children array (truncated) Components rendered in the hover overlay

gridColumns sets the column count per breakpoint — { mobile, sm, md, lg, xl }, each 1 to 6. layout is grid for uniform rows, masonry for variable heights, or carousel for one walkable track.

app.yaml
tables:
  - name: products
    fields:
      - { name: name, type: single-line-text }
      - { name: photo, type: single-attachment }
pages:
  - name: Products
    path: /products
    components:
      - type: gallery
        dataSource: { table: products }
        gridColumns: { mobile: 1, md: 2, lg: 3 }
        layout: masonry
        galleryCard:
          coverImage: '$record.photo'
          aspectRatio: '4:3'
          children:
            - { type: text, element: h3, content: '$record.name' }

The card

galleryCard is where a card's own shape is declared. coverImage is usually a $record.* reference and aspectRatio takes a ratio such as 4:3, 16:9 or 1:1. children are the components rendered in the card body, and hoverOverlay: { children } renders over the card on hover. onClick is the action a click on the card runs.

Because children is an ordinary component tree, a card is composed from the same types a page is: a text for the title, a badge for a status, a description-list for a run of facts. $record.<field> resolves inside all of them.

Behaviour

  • type: 'gallery' is a valid component type in page components
  • dataSource.table binds the gallery to a table
  • card.children supports component children with $record.* vars
  • gridColumns configures responsive grid columns per breakpoint
  • card.coverImage renders a cover image from a record field
  • card.onClick triggers a navigation or modal action on card click
  • layout: 'grid' renders equal-height cards
  • layout: 'masonry' renders variable-height cards
  • dataSource.pagination controls pagination style
  • card.aspectRatio sets the cover image aspect ratio
  • card.hoverOverlay renders action buttons on card hover
  • layout: carousel lays the same cards on one walkable track, keyboard-reachable
  • style: numbered renders a numbered pager reaching every record
  • style: infinite pages as numbered does, losing no record
  • An omitted pagination draws every record and no pager
  • User can complete full gallery workflow (regression)

System read-endpoint data source

  • A system-source gallery whose galleryCard fields are NOT app.tables fields boots successfully (field cross-validation is skipped)
  • With dataSource.system, the gallery fetches system.endpoint and never falls through to /api/tables/:t/records
  • The { items: [...] } envelope is normalized to gallery cards; total falls back to rows length when totalKey is absent
  • The galleryCard template renders against endpoint rows (config drives rendering, not a table schema)
  • dataSource.system.query static params are merged into the endpoint request
  • A system-source gallery offers no record-write affordances (no create/edit/delete)
  • A system-source gallery offers no saved/user views and no user-preferences (read-only, endpoint-shaped)
  • Pagination (loadMore/numbered/infinite) stays ON for a system source — a read-side control that re-reads the endpoint, never the records API
  • The existing dataSource.table binding still renders table rows as cards unchanged (discriminated-union regression)
  • A system-source gallery validates, fetches its endpoint, normalizes the envelope into cards, gates writes off, keeps pagination, 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.

Built with Sovrium