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.

json
{
  "version": "1.0",
  "state": { ... },
  "actions": [ ... ],
  "view": { ... },
  "components": { ... }
}

Root Properties

NameTypeRequiredDefaultDescription
versionstringYes-DSL version identifier. Currently only "1.0" is supported.
type"layout"No-File type identifier. Set to "layout" for layout files.
stateRecord<string, StateField>Yes-Application state definitions. Keys are state names, values are field definitions.
actionsActionDefinition[]Yes-Array of action definitions that describe how state changes.
viewViewNodeYes-The root view node that describes the UI structure.
componentsRecord<string, ComponentDef>No-Reusable component definitions. Keys are component names.
importsRecord<string, string>No-Static data imports. Keys are imported data names, values are relative paths to JSON files.
externalImportsRecord<string, string>No-External module imports from CDN. Keys are module names, values are CDN URLs.
lifecycleLifecycleHooksNo-Lifecycle hook definitions for mount, unmount, and route events.
routeRouteDefinitionNo-Route metadata including path, layout, and meta tags.

State Field Definition

Each state field specifies a type and initial value.

NameTypeRequiredDefaultDescription
type"string" | "number" | "boolean" | "list" | "object"Yes-The data type of the state field.
initialanyYes-The initial value. Must match the declared type.

Example State

json
{
  "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.

NameTypeRequiredDefaultDescription
paramsRecord<string, ParamDef>Yes-Component parameter definitions with types and optional defaults.
viewViewNodeYes-The view tree for this component.

Param Definition

NameTypeRequiredDefaultDescription
type"string" | "number" | "boolean" | "json"Yes-The expected type of the parameter.
requiredbooleanNotrueWhether the parameter is required.

Example Component

json
{
  "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

json
{
  "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.

json
{
  "imports": {
    "config": "../data/config.json",
    "examples": "../data/examples.json"
  }
}

Access imported data using the import expression:

json
{ "expr": "import", "name": "config", "path": "siteName" }

External Imports

The externalImports property allows you to dynamically import external JavaScript modules from CDN at runtime.

json
{
  "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:

json
{
  "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.

NameTypeRequiredDefaultDescription
onMountstringNo-Action name to execute when the component/page is mounted.
onUnmountstringNo-Action name to execute when the component/page is unmounted.
onRouteEnterstringNo-Action name to execute when entering this route.
onRouteLeavestringNo-Action name to execute when leaving this route.

Example

json
{
  "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.

NameTypeRequiredDefaultDescription
pathstringYes-URL path pattern (e.g., "/docs/[slug]")
layoutstringNo-Layout name to wrap this page.
layoutParamsRecord<string, Expression>No-Parameters to pass to the layout.
metaRecord<string, Expression>No-Meta tags for the page (title, description, etc.).

Example

json
{
  "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.

json
{
  "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" } }]
      }
    ]
  }
}