Skip to main content
View as Markdown

Routing & Paths

A page's path is the URL it answers. It is matched literally unless it contains a :param or * segment, in which case it becomes a pattern.

Path Forms

Form Example Matches
Root / The home page.
Static /about That exact path, and nothing else.
Nested /settings/billing Any number of static segments.
Dynamic /blog/:slug One segment, captured as slug.
Catch-all /docs/:rest* The remainder of the path, captured as rest — must be the last segment.
Wildcard /files/* Anything below /files/, captured under no name.

A path must match ^/[a-z0-9-_/:*]*$: it starts with /, and only lowercase letters are allowed. /users/:userId is rejected — write /users/:userid, or better, /users/:id.

Dynamic Segments

A :param segment captures exactly one path segment — it never spans a /. /blog/:slug matches /blog/hello but not /blog/2026/hello; use /blog/:slug* for the latter.

Dynamic routes are usually paired with a collection block, so one page definition generates a route per record:

pages:
  - name: Blog Post
    path: /blog/:slug
    collection: { table: posts, slugField: slug }
    components:
      - { type: text, tag: h1, content: '$record.title' }

See Collection & Markdown Pages for the collection contract.

Record Detail Routes

To serve one record at a URL without generating a route per record, pair a dynamic segment with a single-mode data source. param names the path segment to read the record id from:

pages:
  - name: Task
    path: /tasks/:id
    dataSource:
      table: tasks
      mode: single
      param: id
    components:
      - { type: text, tag: h1, content: '$record.title' }
      - { type: text, content: '$record.description' }

A request whose parameter matches no record returns a 404.

The Language Segment

Sovrium does not prefix your paths for you. When app.languages is configured, the runtime reads the first path segment, and if it matches a configured language code it becomes the active language for $t: translation lookups.

Localized routes are therefore authored explicitly — one page per language, each carrying its own segment:

pages:
  - { name: home-en, path: /en, components: [{ type: hero, content: 'hero.title' }] }
  - { name: home-fr, path: /fr, components: [{ type: hero, content: 'hero.title' }] }

Resolution Order

An incoming request is answered by the first of these that matches:

  1. A real file in the public directory.
  2. A redirect rule.
  3. A page path.
  4. The 404 catch-all.

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