Skip to main content
View as Markdown

Layouts, Sidebars & Access

Two page properties sit outside the component tree: layout wraps the body in chrome, and access decides who is allowed to load the page at all.

The layout Block

layout currently holds one key, sidebar — an array of sidebar sections rendered in the page's <aside>. Each section binds to a table and emits one entry per record.

Section property Description
dataSource Required. { table, filter, sort } — a deliberate subset of the full data source.
template Required. How each record renders as an entry (see below).
activeIndicator Highlights the entry matching the active scope.

template is where the per-record expressions live:

template property Description
label Entry label, supporting $record.<field>.
href Entry link target, supporting $record.<field>.
archivedField Field whose truthy value hides the entry from the sidebar.

activeIndicator accepts exactly one value — $currentUser.activeAssignment — which marks the entry matching the tenant switcher's current scope.

pages:
  - name: Workspace
    path: /workspace
    layout:
      sidebar:
        - dataSource:
            table: projects
            sort: [{ field: name, direction: asc }]
          template:
            label: '$record.name'
            href: '/workspace/$record.slug'
            archivedField: archived
          activeIndicator: '$currentUser.activeAssignment'
    components:
      - { type: container, element: main }

Data-Bound Sidebars

A sidebar filtered on $currentUser.assignments.<table> renders only the records the signed-in user is assigned to; a global admin (isUnrestricted) sees them all. Unlike a page-level data source, a sidebar section that cannot resolve its $currentUser reference is dropped silently rather than failing the request — the page still renders, minus that section.

Access Control

access gates the page. It takes one of four forms:

Form Meaning
'all' Everyone, including anonymous visitors. The default.
'authenticated' Any signed-in user.
['admin', 'editor'] Those role names. At least one entry.
{ require, redirectTo } Any of the above as require, plus a redirect target.

A role-array entry may also be a group reference, group:<name>, validated against app.auth.groups.

pages:
  - name: Billing
    path: /billing
    access: { require: authenticated, redirectTo: /login }
    components:
      - { type: text, tag: h1, content: 'Billing' }

What a Denial Returns

Configuration Anonymous or unauthorized visitor gets
access with redirectTo 302 to that path, with ?redirect=<original path> appended.
access without redirectTo 404, so the page's existence is not disclosed.

Last updated July 27, 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