Routing
File-based routing in Constela
File-Based Routing
Constela uses file-based routing. Files in src/routes/ automatically become routes based on their file names.
| File | Route |
|---|---|
index.json | / |
about.json | /about |
users/index.json | /users |
users/[id].json | /users/:id |
docs/[...slug].json | /docs/* |
Route Definition
Each route file includes a route field that defines metadata:
{
"version": "1.0",
"route": {
"path": "/users/:id",
"layout": "main",
"meta": {
"title": "User Profile",
"description": "View user details"
}
},
"view": { ... }
}Route Fields
| Field | Required | Description |
|---|---|---|
path | Yes | URL path pattern |
layout | No | Layout name to use |
meta | No | Page metadata (title, description) |
Dynamic Routes
Path Parameters
Use [paramName] in file names for dynamic segments:
src/routes/
├── users/
│ └── [id].json → /users/:id
└── posts/
└── [year]/
└── [slug].json → /posts/:year/:slugCatch-All Routes
Use [...paramName] for catch-all routes:
src/routes/
└── docs/
└── [...slug].json → /docs/*Accessing Route Parameters
Access route parameters using the route expression:
{
"kind": "text",
"value": { "expr": "route", "name": "id" }
}Route Expression Sources
| Source | Description | Example |
|---|---|---|
param (default) | Dynamic path segments | /users/:id → { "expr": "route", "name": "id" } |
query | URL query parameters | ?search=test → { "expr": "route", "name": "search", "source": "query" } |
path | Full current path | { "expr": "route", "source": "path" } |
Query Parameters
Access query string values with source: "query":
{
"kind": "text",
"value": { "expr": "route", "name": "search", "source": "query" }
}For a URL like /products?category=electronics&page=2:
{
"kind": "element",
"tag": "div",
"children": [
{
"kind": "text",
"value": { "expr": "route", "name": "category", "source": "query" }
}
]
}Navigation
Link Element
Use anchor elements for navigation:
{
"kind": "element",
"tag": "a",
"props": {
"href": { "expr": "lit", "value": "/about" }
},
"children": [
{ "kind": "text", "value": { "expr": "lit", "value": "About Us" } }
]
}Programmatic Navigation
Navigate programmatically using the navigate action step:
{
"actions": [
{
"name": "goToUser",
"steps": [
{
"do": "navigate",
"url": { "expr": "lit", "value": "/users/123" }
}
]
}
]
}With dynamic paths:
{
"actions": [
{
"name": "viewUserProfile",
"steps": [
{
"do": "navigate",
"url": {
"expr": "bin",
"op": "+",
"left": { "expr": "lit", "value": "/users/" },
"right": { "expr": "state", "name": "selectedUserId" }
}
}
]
}
]
}Route Lifecycle Hooks
Execute actions when entering or leaving routes:
{
"lifecycle": {
"onRouteEnter": "loadPageData",
"onRouteLeave": "saveProgress"
}
}Available Route Hooks
| Hook | Description |
|---|---|
onRouteEnter | Executed when navigating to this route |
onRouteLeave | Executed when navigating away from this route |
Example: Load Data on Route Enter
{
"version": "1.0",
"route": {
"path": "/users/:id"
},
"lifecycle": {
"onRouteEnter": "loadUserData"
},
"state": {
"user": { "type": "object", "initial": {} },
"loading": { "type": "boolean", "initial": true }
},
"actions": [
{
"name": "loadUserData",
"steps": [
{
"do": "set",
"target": "loading",
"value": { "expr": "lit", "value": true }
},
{
"do": "fetch",
"url": {
"expr": "bin",
"op": "+",
"left": { "expr": "lit", "value": "/api/users/" },
"right": { "expr": "route", "name": "id" }
},
"onSuccess": {
"steps": [
{
"do": "set",
"target": "user",
"value": { "expr": "var", "name": "response" }
},
{
"do": "set",
"target": "loading",
"value": { "expr": "lit", "value": false }
}
]
}
}
]
}
],
"view": {
"kind": "element",
"tag": "div",
"children": [
{
"kind": "if",
"condition": { "expr": "state", "name": "loading" },
"then": {
"kind": "text",
"value": { "expr": "lit", "value": "Loading..." }
},
"else": {
"kind": "element",
"tag": "h1",
"children": [
{
"kind": "text",
"value": {
"expr": "get",
"base": { "expr": "state", "name": "user" },
"path": "name"
}
}
]
}
}
]
}
}Tip
Use onRouteEnter to fetch data specific to the route. Use onRouteLeave to save unsaved changes or clean up resources before navigating away.
Layouts
Use layouts to share common UI across multiple routes:
{
"route": {
"path": "/dashboard",
"layout": "dashboard"
}
}Create a layout file at src/layouts/dashboard.json:
{
"version": "1.0",
"type": "layout",
"view": {
"kind": "element",
"tag": "div",
"children": [
{
"kind": "element",
"tag": "nav",
"children": [
{
"kind": "element",
"tag": "a",
"props": { "href": { "expr": "lit", "value": "/" } },
"children": [
{ "kind": "text", "value": { "expr": "lit", "value": "Home" } }
]
}
]
},
{ "kind": "slot" }
]
}
}Note
The slot node marks where the page content will be rendered within the layout.
Summary
Constela's file-based routing provides:
- Automatic routes: File structure defines URL structure
- Dynamic parameters: Use
[param]and[...param]syntax - Route expressions: Access params and query strings in views
- Programmatic navigation: Navigate from actions
- Lifecycle hooks: Load data on route enter/leave
- Layouts: Share common UI across routes