DSL Root Schema
Complete specification of the Constela program structure
Overview
Every Constela program is a single JSON object that follows a specific schema. This document describes the top-level structure that all Constela programs must adhere to.
{
"version": "1.0",
"state": { ... },
"actions": [ ... ],
"view": { ... },
"components": { ... }
}Root Properties
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| version | string | Yes | - | DSL version identifier. Currently only "1.0" is supported. |
| type | "layout" | No | - | File type identifier. Set to "layout" for layout files. |
| state | Record<string, StateField> | Yes | - | Application state definitions. Keys are state names, values are field definitions. |
| actions | ActionDefinition[] | Yes | - | Array of action definitions that describe how state changes. |
| view | ViewNode | Yes | - | The root view node that describes the UI structure. |
| components | Record<string, ComponentDef> | No | - | Reusable component definitions. Keys are component names. |
| imports | Record<string, string> | No | - | Static data imports. Keys are imported data names, values are relative paths to JSON files. |
| externalImports | Record<string, string> | No | - | External module imports from CDN. Keys are module names, values are CDN URLs. |
| lifecycle | LifecycleHooks | No | - | Lifecycle hook definitions for mount, unmount, and route events. |
| route | RouteDefinition | No | - | Route metadata including path, layout, and meta tags. |
State Field Definition
Each state field specifies a type and initial value.
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| type | "string" | "number" | "boolean" | "list" | "object" | Yes | - | The data type of the state field. |
| initial | any | Yes | - | The initial value. Must match the declared type. |
Example State
{
"state": {
"count": { "type": "number", "initial": 0 },
"message": { "type": "string", "initial": "Hello" },
"items": { "type": "list", "initial": [] },
"user": { "type": "object", "initial": { "name": "", "age": 0 } },
"isActive": { "type": "boolean", "initial": false }
}
}Component Definition
Components allow you to create reusable UI pieces.
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| params | Record<string, ParamDef> | Yes | - | Component parameter definitions with types and optional defaults. |
| view | ViewNode | Yes | - | The view tree for this component. |
Param Definition
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| type | "string" | "number" | "boolean" | "json" | Yes | - | The expected type of the parameter. |
| required | boolean | No | true | Whether the parameter is required. |
Example Component
{
"components": {
"Button": {
"params": {
"label": { "type": "string" },
"variant": { "type": "string" }
},
"view": {
"kind": "element",
"tag": "button",
"props": {
"class": { "expr": "param", "name": "variant" }
},
"children": [
{ "kind": "text", "value": { "expr": "param", "name": "label" } }
]
}
}
}
}Complete Example
{
"version": "1.0",
"state": {
"count": { "type": "number", "initial": 0 }
},
"actions": [
{
"name": "increment",
"steps": [
{ "do": "update", "target": "count", "operation": "increment" }
]
}
],
"view": {
"kind": "element",
"tag": "div",
"children": [
{
"kind": "text",
"value": { "expr": "state", "name": "count" }
},
{
"kind": "element",
"tag": "button",
"props": {
"onClick": { "event": "click", "action": "increment" }
},
"children": [
{ "kind": "text", "value": { "expr": "lit", "value": "+1" } }
]
}
]
}
}Imports
The imports property allows you to load static JSON data files at build time.
{
"imports": {
"config": "../data/config.json",
"examples": "../data/examples.json"
}
}Access imported data using the import expression:
{ "expr": "import", "name": "config", "path": "siteName" }External Imports
The externalImports property allows you to dynamically import external JavaScript modules from CDN at runtime.
{
"externalImports": {
"monaco-editor": "https://cdn.jsdelivr.net/npm/monaco-editor@0.52.0/+esm",
"chart.js": "https://cdn.jsdelivr.net/npm/chart.js@4.4.0/+esm"
}
}Use with the import action step to load modules dynamically:
{
"do": "import",
"module": "monaco-editor",
"result": "monaco",
"onSuccess": [
{
"do": "call",
"target": { "expr": "var", "name": "monaco", "path": "editor.create" },
"args": [{ "expr": "ref", "name": "editorContainer" }]
}
]
}Lifecycle Hooks
The lifecycle property defines actions to execute at specific points in the component or page lifecycle.
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| onMount | string | No | - | Action name to execute when the component/page is mounted. |
| onUnmount | string | No | - | Action name to execute when the component/page is unmounted. |
| onRouteEnter | string | No | - | Action name to execute when entering this route. |
| onRouteLeave | string | No | - | Action name to execute when leaving this route. |
Example
{
"lifecycle": {
"onMount": "initEditor",
"onUnmount": "cleanup"
},
"actions": [
{
"name": "initEditor",
"steps": [
{ "do": "import", "module": "monaco-editor", "result": "monaco" }
]
},
{
"name": "cleanup",
"steps": [
{ "do": "dispose", "target": { "expr": "state", "name": "editor" } }
]
}
]
}Route Definition
The route property defines routing metadata for a page. Used with @constela/start.
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| path | string | Yes | - | URL path pattern (e.g., "/docs/[slug]") |
| layout | string | No | - | Layout name to wrap this page. |
| layoutParams | Record<string, Expression> | No | - | Parameters to pass to the layout. |
| meta | Record<string, Expression> | No | - | Meta tags for the page (title, description, etc.). |
Example
{
"route": {
"path": "/blog/[slug]",
"layout": "docs",
"meta": {
"title": { "expr": "data", "name": "post", "path": "frontmatter.title" },
"description": { "expr": "data", "name": "post", "path": "frontmatter.description" }
}
}
}Layout Files
Layout files use "type": "layout" and must contain at least one slot node for page content injection.
{
"version": "1.0",
"type": "layout",
"state": {
"theme": { "type": "string", "initial": "light" }
},
"actions": [],
"view": {
"kind": "element",
"tag": "div",
"props": { "class": { "expr": "lit", "value": "layout" } },
"children": [
{
"kind": "element",
"tag": "header",
"children": [{ "kind": "text", "value": { "expr": "lit", "value": "Site Header" } }]
},
{
"kind": "element",
"tag": "main",
"children": [{ "kind": "slot" }]
},
{
"kind": "element",
"tag": "footer",
"children": [{ "kind": "text", "value": { "expr": "lit", "value": "Site Footer" } }]
}
]
}
}