Expressions
Complete reference for all expression types in Constela
Overview
Expressions are the way to work with dynamic values in Constela. They can represent literal values, state access, variable access, and computed values.
Literal Expression
Represents a static, constant value.
{ "expr": "lit", "value": "Hello, World!" }
{ "expr": "lit", "value": 42 }
{ "expr": "lit", "value": true }
{ "expr": "lit", "value": ["a", "b", "c"] }| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| expr | "lit" | Yes | - | Expression type identifier. |
| value | any | Yes | - | The literal value (string, number, boolean, array, or object). |
State Expression
Accesses a value from the application state.
{ "expr": "state", "name": "count" }
{ "expr": "state", "name": "user" }| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| expr | "state" | Yes | - | Expression type identifier. |
| name | string | Yes | - | Name of the state field to access. |
Tip
To access nested properties of a state field, use the get expression instead.
Nested Property Access
Use the get expression to access properties within state objects:
// Access state.user.profile.name
{
"expr": "get",
"base": { "expr": "state", "name": "user" },
"path": "profile.name"
}
// Access state.settings.theme
{
"expr": "get",
"base": { "expr": "state", "name": "settings" },
"path": "theme"
}Variable Expression
Accesses a loop variable or component prop.
{ "expr": "var", "name": "item" }
{ "expr": "var", "name": "user", "path": "email" }| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| expr | "var" | Yes | - | Expression type identifier. |
| name | string | Yes | - | Name of the variable (from each loop's 'as' or component props). |
| path | string | No | - | Dot-notation path for nested property access. |
Usage in Each Loop
{
"kind": "each",
"items": { "expr": "state", "name": "users" },
"as": "user",
"index": "i",
"body": {
"kind": "text",
"value": { "expr": "var", "name": "user", "path": "name" }
}
}Binary Expression
Computes a value from two operands and an operator.
{
"expr": "bin",
"op": "+",
"left": { "expr": "state", "name": "count" },
"right": { "expr": "lit", "value": 1 }
}| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| expr | "bin" | Yes | - | Expression type identifier. |
| op | string | Yes | - | Binary operator (see operators table below). |
| left | Expression | Yes | - | Left operand expression. |
| right | Expression | Yes | - | Right operand expression. |
Binary Operators
| Operator | Description | Example |
|---|---|---|
+ | Addition / String concatenation | 5 + 3 = 8, "a" + "b" = "ab" |
- | Subtraction | 5 - 3 = 2 |
* | Multiplication | 5 * 3 = 15 |
/ | Division | 6 / 3 = 2 |
== | Equality | 5 == 5 is true |
!= | Inequality | 5 != 3 is true |
< | Less than | 3 < 5 is true |
<= | Less than or equal | 3 <= 3 is true |
> | Greater than | 5 > 3 is true |
>= | Greater than or equal | 5 >= 5 is true |
&& | Logical AND | true && false is false |
|| | Logical OR | true || false is true |
Complex Expression Example
{
"expr": "bin",
"op": "&&",
"left": {
"expr": "bin",
"op": ">",
"left": { "expr": "state", "name": "count" },
"right": { "expr": "lit", "value": 0 }
},
"right": {
"expr": "bin",
"op": "<",
"left": { "expr": "state", "name": "count" },
"right": { "expr": "lit", "value": 100 }
}
}This evaluates to true when count > 0 && count < 100.
Not Expression
Negates a boolean expression.
{
"expr": "not",
"operand": { "expr": "state", "name": "isLoading" }
}| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| expr | "not" | Yes | - | Expression type identifier. |
| operand | Expression | Yes | - | Expression to negate (should evaluate to boolean). |
Example
{
"kind": "if",
"condition": {
"expr": "not",
"operand": { "expr": "state", "name": "isHidden" }
},
"then": {
"kind": "text",
"value": { "expr": "lit", "value": "Visible content" }
}
}Param Expression
Accesses action parameters (used inside action steps).
{ "expr": "param", "name": "value" }
{ "expr": "param", "name": "event", "path": "target.value" }| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| expr | "param" | Yes | - | Expression type identifier. |
| name | string | Yes | - | Name of the action parameter. |
| path | string | No | - | Dot-notation path for nested property access. |
Usage in Actions
{
"name": "updateInput",
"steps": [
{
"do": "set",
"target": "inputValue",
"value": { "expr": "param", "name": "event", "path": "target.value" }
}
]
}Note
The param expression is primarily used within action steps to access event data or other runtime parameters.
Cond Expression
Returns different values based on a condition. Unlike the if node which controls rendering, cond evaluates to a value.
{
"expr": "cond",
"if": { "expr": "state", "name": "done" },
"then": { "expr": "lit", "value": "Completed" },
"else": { "expr": "lit", "value": "Pending" }
}| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| expr | "cond" | Yes | - | Expression type identifier. |
| if | Expression | Yes | - | Condition expression (must evaluate to boolean). |
| then | Expression | Yes | - | Expression to evaluate when condition is true. |
| else | Expression | Yes | - | Expression to evaluate when condition is false. |
Cond vs If Node
| Feature | cond Expression | if Node |
|---|---|---|
| Purpose | Compute a value | Control rendering |
| Returns | A value | A view node |
| Usage | Inside text, attributes, props | View tree |
Examples
// Conditional text label
{
"kind": "text",
"value": {
"expr": "cond",
"if": { "expr": "state", "name": "isAdmin" },
"then": { "expr": "lit", "value": "Admin Panel" },
"else": { "expr": "lit", "value": "User Dashboard" }
}
}
// Conditional CSS class
{
"kind": "element",
"tag": "div",
"props": {
"class": {
"expr": "cond",
"if": { "expr": "state", "name": "isActive" },
"then": { "expr": "lit", "value": "active" },
"else": { "expr": "lit", "value": "inactive" }
}
}
}
// Nested cond for multiple conditions
{
"expr": "cond",
"if": {
"expr": "bin",
"op": ">",
"left": { "expr": "state", "name": "score" },
"right": { "expr": "lit", "value": 80 }
},
"then": { "expr": "lit", "value": "Excellent" },
"else": {
"expr": "cond",
"if": {
"expr": "bin",
"op": ">",
"left": { "expr": "state", "name": "score" },
"right": { "expr": "lit", "value": 50 }
},
"then": { "expr": "lit", "value": "Good" },
"else": { "expr": "lit", "value": "Needs Improvement" }
}
}Tip
Use cond when you need conditional values inside text, attributes, or props. Use if nodes when you need to conditionally render entire elements.
Get Expression
Accesses properties from an object expression. Essential for working with object arrays in each loops.
{
"expr": "get",
"base": { "expr": "var", "name": "item" },
"path": "title"
}| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| expr | "get" | Yes | - | Expression type identifier. |
| base | Expression | Yes | - | Base expression (typically a variable or state). |
| path | string | Yes | - | Dot-separated path to the property. |
Path Access
The path supports dot notation for nested properties:
// Access item.user.profile.name
{
"expr": "get",
"base": { "expr": "var", "name": "item" },
"path": "user.profile.name"
}Note
If a property in the path doesn't exist, the expression returns undefined. This allows safe access to optional properties.
Get vs Var with Path
Both get and var with path can access nested properties. Use get when the base is a computed expression, and var with path when accessing a simple loop variable.
// Using var with path (simpler for loop variables)
{ "expr": "var", "name": "user", "path": "email" }
// Using get (when base is computed or for clarity)
{
"expr": "get",
"base": { "expr": "var", "name": "user" },
"path": "email"
}Usage in Each Loop
{
"kind": "each",
"items": { "expr": "state", "name": "todos" },
"as": "todo",
"index": "i",
"body": {
"kind": "element",
"tag": "div",
"children": [
{
"kind": "text",
"value": {
"expr": "get",
"base": { "expr": "var", "name": "todo" },
"path": "title"
}
},
{
"kind": "text",
"value": {
"expr": "cond",
"if": {
"expr": "get",
"base": { "expr": "var", "name": "todo" },
"path": "done"
},
"then": { "expr": "lit", "value": " [Completed]" },
"else": { "expr": "lit", "value": " [Pending]" }
}
}
]
}
}Route Expression
Accesses route parameters, query strings, or the current path in routed applications.
{ "expr": "route", "name": "id" }
{ "expr": "route", "name": "search", "source": "query" }
{ "expr": "route", "source": "path" }| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| expr | "route" | Yes | - | Expression type identifier. |
| name | string | No | - | Name of the route parameter or query parameter. |
| source | "param" | "query" | "path" | No | "param" | Source of the route value. |
Source Types
| Source | Description | Example URL | Expression |
|---|---|---|---|
param | Dynamic path segment | /posts/123 | { "expr": "route", "name": "id" } |
query | URL query parameter | /search?q=test | { "expr": "route", "name": "q", "source": "query" } |
path | Full current path | /docs/intro | { "expr": "route", "source": "path" } |
Example
{
"route": {
"path": "/blog/[slug]"
},
"view": {
"kind": "element",
"tag": "h1",
"children": [{
"kind": "text",
"value": { "expr": "route", "name": "slug" }
}]
}
}Index Expression
Dynamically accesses array elements or object properties using a computed key.
{
"expr": "index",
"base": { "expr": "state", "name": "items" },
"key": { "expr": "state", "name": "selectedIndex" }
}| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| expr | "index" | Yes | - | Expression type identifier. |
| base | Expression | Yes | - | Base expression (array or object). |
| key | Expression | Yes | - | Key expression (number for arrays, string for objects). |
Array Access
{
"expr": "index",
"base": { "expr": "state", "name": "users" },
"key": { "expr": "lit", "value": 0 }
}Object Access with Dynamic Key
{
"expr": "index",
"base": { "expr": "import", "name": "translations" },
"key": { "expr": "state", "name": "currentLanguage" }
}Tip
Use index when the key is dynamic (computed at runtime). Use get when the key is a static string known at compile time.
Import Expression
Accesses data from imported JSON files defined in the imports property.
{ "expr": "import", "name": "config" }
{ "expr": "import", "name": "config", "path": "siteName" }| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| expr | "import" | Yes | - | Expression type identifier. |
| name | string | Yes | - | Name of the import (key in the imports object). |
| path | string | No | - | Dot-notation path for nested property access. |
Example
{
"imports": {
"config": "../data/config.json",
"nav": "../data/navigation.json"
},
"view": {
"kind": "element",
"tag": "header",
"children": [{
"kind": "text",
"value": { "expr": "import", "name": "config", "path": "siteName" }
}]
}
}Data Expression
Accesses data from build-time data sources (glob, file, or API). Used primarily with @constela/start for SSG.
{ "expr": "data", "name": "post" }
{ "expr": "data", "name": "post", "path": "frontmatter.title" }| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| expr | "data" | Yes | - | Expression type identifier. |
| name | string | Yes | - | Name of the data source (key in the data object). |
| path | string | No | - | Dot-notation path for nested property access. |
Example with MDX Data Source
{
"data": {
"docs": {
"type": "glob",
"pattern": "content/docs/*.mdx",
"transform": "mdx"
}
},
"view": {
"kind": "element",
"tag": "article",
"children": [
{
"kind": "element",
"tag": "h1",
"children": [{
"kind": "text",
"value": { "expr": "data", "name": "docs", "path": "frontmatter.title" }
}]
},
{
"kind": "markdown",
"content": { "expr": "data", "name": "docs", "path": "content" }
}
]
}
}Ref Expression
References a DOM element by its ref attribute. Used for JavaScript interop.
{ "expr": "ref", "name": "editorContainer" }| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| expr | "ref" | Yes | - | Expression type identifier. |
| name | string | Yes | - | Name of the ref (matches the ref attribute on an element). |
Example
{
"view": {
"kind": "element",
"tag": "div",
"props": {
"ref": { "expr": "lit", "value": "editorContainer" }
}
},
"lifecycle": {
"onMount": "initEditor"
},
"actions": [{
"name": "initEditor",
"steps": [{
"do": "call",
"target": { "expr": "var", "name": "monaco", "path": "editor.create" },
"args": [
{ "expr": "ref", "name": "editorContainer" },
{ "expr": "lit", "value": { "language": "json" } }
]
}]
}]
}Note
The ref expression is primarily used with external JavaScript libraries that need direct DOM access, such as code editors, charts, or maps.
Style Expression
Applies style presets with variants, similar to CVA (Class Variance Authority) or Tailwind Variants.
{
"expr": "style",
"name": "button",
"variants": {
"variant": { "expr": "lit", "value": "primary" },
"size": { "expr": "lit", "value": "lg" }
}
}| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| expr | "style" | Yes | - | Expression type identifier. |
| name | string | Yes | - | Name of the style preset defined in the styles object. |
| variants | Record<string, Expression> | No | - | Variant selections to apply. Values can be literal or dynamic expressions. |
Example
{
"styles": {
"button": {
"base": "px-4 py-2 rounded font-medium",
"variants": {
"variant": {
"primary": "bg-blue-500 text-white",
"secondary": "bg-gray-200 text-gray-800"
},
"size": {
"sm": "text-sm",
"md": "text-base",
"lg": "text-lg"
}
},
"defaultVariants": {
"variant": "primary",
"size": "md"
}
}
},
"view": {
"kind": "element",
"tag": "button",
"props": {
"className": {
"expr": "style",
"name": "button",
"variants": {
"variant": { "expr": "lit", "value": "primary" }
}
}
},
"children": [{ "kind": "text", "value": { "expr": "lit", "value": "Click me" } }]
}
}Dynamic Variants
Combine with state for dynamic styling:
{
"state": {
"isActive": { "type": "boolean", "initial": false }
},
"view": {
"props": {
"className": {
"expr": "style",
"name": "button",
"variants": {
"variant": {
"expr": "cond",
"if": { "expr": "state", "name": "isActive" },
"then": { "expr": "lit", "value": "primary" },
"else": { "expr": "lit", "value": "secondary" }
}
}
}
}
}
}Tip
Style expressions are resolved at compile time when all variants are static literals. Dynamic variants are resolved at runtime. See the Style System reference for complete documentation.
Concat Expression
Concatenates multiple expressions into a single string. Each expression is evaluated and converted to a string, then joined together.
{
"expr": "concat",
"items": [
{ "expr": "lit", "value": "Hello, " },
{ "expr": "var", "name": "user", "path": "name" },
{ "expr": "lit", "value": "!" }
]
}| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| expr | "concat" | Yes | - | Expression type identifier. |
| items | Expression[] | Yes | - | Array of expressions to concatenate. Each is evaluated and converted to string. |
Dynamic Class Names
A common use case is building dynamic CSS class names:
{
"kind": "element",
"tag": "div",
"props": {
"class": {
"expr": "concat",
"items": [
{ "expr": "lit", "value": "avatar rounded-full " },
{ "expr": "var", "name": "user", "path": "avatarSize" },
{ "expr": "lit", "value": " " },
{ "expr": "var", "name": "user", "path": "avatarColor" }
]
}
}
}Combining with Conditionals
{
"expr": "concat",
"items": [
{ "expr": "lit", "value": "btn " },
{
"expr": "cond",
"if": { "expr": "state", "name": "isActive" },
"then": { "expr": "lit", "value": "btn-active" },
"else": { "expr": "lit", "value": "btn-inactive" }
}
]
}Note
Null and undefined values are converted to empty strings. All other values are converted using JavaScript's String() function.