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

```yaml
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`.

```yaml
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.               |

:::callout
**`access` never answers `401`.** It redirects or hides. The only source of a `401` on a page is an unresolvable `$currentUser` reference in a [data source](/en/docs/pages-data-binding#current-user-scoping) — two different mechanisms with two different answers.
:::

## Related Pages

- [Pages Overview](/en/docs/pages-overview) — the full page property table.
- [Data Binding](/en/docs/pages-data-binding) — `$currentUser` scoping and its `401`.
- [Roles & RBAC](/en/docs/auth-roles-rbac) — the role names `access` accepts.
- [Groups](/en/docs/auth-groups) — the `group:` references `access` accepts.
- [Layout Components](/en/docs/layout-components) — the `sidebar` component rendered inside the body.
