Pages Overview
Pages are server-rendered (SSR) using a declarative component tree. Each page declares a name, a path, optional SEO meta, and a components array of nested components. Pages render your tables' records, host forms that write to them, embed interactive islands, and expose public-facing content — all from configuration, no custom React required.
A page needs at minimum a name and a path. Everything else — metadata, access control, scripts, dynamic collection routes, markdown content, view transitions, and per-page toasts — is optional and layered on top.
pages:
- name: Home
path: /
meta:
title: Welcome
description: The fastest way to ship internal tools.
components:
- type: hero
content: Build apps from config
- type: container
children:
- { type: text, content: 'Get started in minutes.' }
Page Properties
Each entry in the pages array accepts the following properties.
| Property | Description |
|---|---|
id |
Optional unique identifier for the page. Auto-generated when omitted. |
name |
Human-readable page name (used in navigation and admin listings). |
path |
URL route. Static (/about), root (/), or dynamic with a :param segment (/blog/:slug). See Routing. |
meta |
SEO and document-head metadata: title, description, Open Graph, Twitter cards, favicons, structured data. See SEO & Metadata. |
components |
Array of component definitions forming the page's render tree. See the component model. |
access |
Access control: 'all' (public), 'authenticated', a role array ['admin'], or { require, redirectTo }. Defaults to 'all'. |
collection |
Template configuration that generates one route per table record (dynamic pages). See Collection Pages. |
markdown |
Render the entire page body from a markdown file or inline string, with layout and table-of-contents options. See Markdown Pages. |
source |
File-based content source (source.file) for a content component — loads markdown from disk with frontmatter. Mutually exclusive with content. |
layout |
Named layout sections (e.g. a data-bound sidebar) wrapping the page body. See Layouts & Sidebars. |
scripts |
Client-side scripts, feature flags, external dependencies, and config data injected into the page. See Interactivity & Scripts. |
vars |
Page-scoped variables (key/value pairs) referenceable via $vars.* in component templates. |
toasts |
Page-level toast notification config (position, default duration). |
viewTransition |
Page navigation animation: fade, slide (with direction), or none, plus duration. |
sitemap |
Per-page sitemap entry (priority, change frequency) or false to exclude the page from the sitemap. |
rss |
Enable an RSS feed for a collection page — true for defaults, or { limit } to cap item count. |
pages:
- name: About
path: /about
access: all
meta: { title: 'About Us' }
viewTransition: { type: fade, duration: 200 }
components:
- { type: text, tag: h1, content: 'About' }
Routing
The path property declares the URL where a page is served.
| Path form | Example | Description |
|---|---|---|
| Root | / |
The home page. |
| Static | /about |
A fixed route. |
| Nested static | /checkout |
Any number of static segments. |
| Dynamic | /blog/:slug |
A :param segment captures a value available to the page as a route param. |
| Record id | /tasks/$id |
Dynamic detail routes resolve the matching record into $record.*. |
Dynamic routes are most often paired with a collection block so one page definition generates a route per record. See Collection Pages.
The Component Model
A page's components array is a tree. Every component has a type (one of ~80 built-in component types) and a set of properties. Components compose through a shared module system, so the same cross-cutting properties are available almost everywhere:
| Shared property | Available on | Description |
|---|---|---|
props |
Almost all components | Key-value bag of styling/config attributes rendered as HTML attributes (e.g. className, variant). |
content |
Content/layout components | Inline text or markdown; supports $record.*, $vars.*, and key (translation) substitution. |
children |
Container-like components | Nested child component definitions. |
dataSource |
Data-bound components | Binds the component to a table (filter, sort, pagination, single/search mode). See Data Binding. |
visibility |
Most components | Conditional display based on auth state — server-rendered, hidden client-side when conditions are unmet. |
responsive |
Most components | Per-breakpoint overrides for responsive behavior. |
interactions |
Interactive components | Click actions, hover effects, and entrance animations. See Interactivity & Scripts. |
action |
Form/button components | Behavior to run on interaction — crud, auth, navigate, automation, with onSuccess/onError handlers. |
i18n |
Content components | Localized content variants per language. |
Variable substitution. Component content and props values resolve three reference families at render time: $record.<field> (current data-source record), $vars.<key> / $currentUser.<path> (page and session context), and key (translation keys, with fallback). The page-level markdown source additionally exposes $frontmatter.*.
Components are grouped into categories, each documented on its own page:
| Category | Examples |
|---|---|
| Layout | hero, container, flex, grid, responsiveGrid, card, sidebar, modal, tab-panel |
| Content | text, code, toc, accordion, blockquote, alert, custom-html |
| Data | data-table, list, gallery, kanban, calendar, chart, kpi, timeline |
| Form Controls | input, textarea, select, checkbox, radio, switch, slider, date-picker, file-upload, number-input |
| Overlays | dialog, drawer, popover, tooltip, hover-card, alert-dialog, toast, progress, skeleton |
| Navigation | navigation-menu, dropdown-menu, breadcrumb, tabs, pagination, toggle, button-group, menubar |
| Media | image, icon, video, audio, iframe, carousel, aspect-ratio |
| Display | static-table, command, empty-state, resizable, scroll-area, speech-bubble, status-indicator |
| Feedback | progress, spinner, skeleton |
| Interactive | button, link, alert, badge, button-group |
| Specialty | wizard, reorderable-list, language-switcher, comments, ai-chat |
| Social | comments, commentCount, ai-chat, sharing |
| SEO & Metadata | title, description, Open Graph, Twitter cards, JSON-LD, favicons |
| Interactivity & Scripts | scripts, interactions, auto-save, custom-html |
Data Binding
Bind any data component (or container) to a table with dataSource. The bound records expose their fields to descendants through $record.<field> references.
dataSource property |
Description |
|---|---|
table |
Table name to bind to (validated against app.tables). |
fields |
Specific fields to fetch (omit to fetch all). |
filter |
Array of { field, operator, value } conditions combined with AND. Operators: eq, neq, contains, gt, lt, gte, lte. |
sort |
Array of { field, direction } rules applied in order (primary, secondary). |
pagination |
{ pageSize, style } — style: numbered renders page navigation; style: loadMore renders a Load More button. |
mode |
single resolves one record by a route param; search filters across searchFields with debounce and maxResults. |
id |
Identifier for cross-component data-source references. |
- type: data-table
dataSource:
table: tasks
filter:
- { field: status, operator: neq, value: archived }
sort:
- { field: created_at, direction: desc }
pagination: { pageSize: 25, style: numbered }
Current-user scoping. $currentUser.* resolves per-request during SSR (never cached across users): $currentUser.id, $currentUser.role, $currentUser.email, $currentUser.isUnrestricted (global-admin bypass), and $currentUser.assignments.<table> (flattened record ids the user is assigned to). Pages using $currentUser return 401 when accessed unauthenticated.
Collection Pages
A collection block turns one page definition into a route per table record. The page's path must include a dynamic :param segment matching slugField.
collection property |
Description |
|---|---|
table |
Table name to generate collection pages from (must exist in app.tables). |
slugField |
Field whose value becomes the URL parameter (e.g. slug, id). |
filter |
Optional { field, operator, value } conditions limiting which records generate pages. |
pages:
- name: Blog Post
path: /blog/:slug
collection:
table: posts
slugField: slug
filter:
- { field: published, operator: eq, value: true }
rss: { limit: 20 }
components:
- { type: text, tag: h1, content: '$record.title' }
- { type: text, content: '$record.body', props: { format: markdown } }
A request to /blog/123 that does not match any record returns a 404.
Markdown Pages
Render a whole page from markdown — either inline or from a file — with frontmatter, layout, and an auto-generated table of contents.
markdown property |
Description |
|---|---|
content |
Inline markdown string. |
file |
Path to a markdown file (e.g. content/docs.md), resolved from project root. Mutually exclusive with content. |
layout |
Wrapper style: prose (default), docs, full, or none. |
toc |
Table-of-contents config — true for defaults, or { maxDepth, position } (inline or sidebar). |
YAML frontmatter (between --- delimiters) is parsed and exposed as $frontmatter.* in meta and sibling properties. Markdown HTML is sanitized to prevent XSS (consistent with the text/alert content components).
pages:
- name: Docs Home
path: /docs
markdown:
file: content/docs/home.md
layout: docs
toc: { maxDepth: 3, position: sidebar }
For the contentDir collection (one page per markdown file in a directory, with a derived sidebar), use a content component with a contentDir source:
contentDir property |
Description |
|---|---|
directory |
Directory containing markdown content files. |
slugFrom |
Derive URL slug from filename or filepath. |
include |
Glob pattern to filter files (e.g. *.md). |
sort |
{ field, order } — sort by a frontmatter field (e.g. date, desc). |
filter |
Filter conditions on frontmatter fields. |
nav |
Sidebar config derived from the collection: { enabled, groupBy, labelFrom }. |
Layouts & Sidebars
The layout property wraps the page body in named sections. The most common is a data-bound sidebar rendered inside the page <aside>.
| Sidebar section property | Description |
|---|---|
dataSource |
Binds entries to a table with { table, filter, sort }. |
label |
Per-record label expression (supports $record.<field>). |
href |
Per-record anchor href (supports $record.<field>). |
archivedField |
Field whose truthy value hides an entry from the sidebar. |
activeIndicator |
Highlights the entry matching the active scope (e.g. tenant-switcher cookie). |
A scoped sidebar bound to $currentUser.assignments.<table> renders one entry per assigned record; a global admin (isUnrestricted=true) sees all records.
Access Control
The access property gates a page:
| Form | Meaning |
|---|---|
'all' |
Everyone, including unauthenticated visitors (default). |
'authenticated' |
Any logged-in user. |
['admin', 'editor'] |
Specific role names. |
{ require: 'authenticated', redirectTo: '/login' } |
Redirect unauthorized users instead of returning an error. |
Related Pages
- Layout Components — structural building blocks.
- Content Components — text, code, callouts, accordions.
- Data Components — tables, lists, charts, KPIs bound to your data.
- Form Controls — inputs, selects, pickers, toggles.
- SEO & Metadata — document head, social cards, structured data.
- Interactivity & Scripts — actions, animations, auto-save.
- Tables Overview — the data models pages render.
- Forms — standalone form definitions.
- Search — full-text and public search configuration.
- Theme — design tokens consumed by component styling.