Your First App
Build a counter app
Let's build a classic counter application to understand Constela's core concepts.
The Counter App
Here's the complete src/routes/counter.json for a counter:
{
"version": "1.0",
"route": {
"path": "/counter"
},
"state": {
"count": { "type": "number", "initial": 0 }
},
"actions": [
{
"name": "increment",
"steps": [
{ "do": "update", "target": "count", "operation": "increment" }
]
},
{
"name": "decrement",
"steps": [
{ "do": "update", "target": "count", "operation": "decrement" }
]
},
{
"name": "reset",
"steps": [
{ "do": "set", "target": "count", "value": { "expr": "lit", "value": 0 } }
]
}
],
"view": {
"kind": "element",
"tag": "div",
"children": [
{
"kind": "element",
"tag": "h1",
"children": [
{ "kind": "text", "value": { "expr": "lit", "value": "Counter" } }
]
},
{
"kind": "element",
"tag": "p",
"children": [
{ "kind": "text", "value": { "expr": "lit", "value": "Count: " } },
{ "kind": "text", "value": { "expr": "state", "name": "count" } }
]
},
{
"kind": "element",
"tag": "div",
"children": [
{
"kind": "element",
"tag": "button",
"props": { "onClick": { "event": "click", "action": "decrement" } },
"children": [
{ "kind": "text", "value": { "expr": "lit", "value": "-" } }
]
},
{
"kind": "element",
"tag": "button",
"props": { "onClick": { "event": "click", "action": "reset" } },
"children": [
{ "kind": "text", "value": { "expr": "lit", "value": "Reset" } }
]
},
{
"kind": "element",
"tag": "button",
"props": { "onClick": { "event": "click", "action": "increment" } },
"children": [
{ "kind": "text", "value": { "expr": "lit", "value": "+" } }
]
}
]
}
]
}
}Understanding the Structure
Every Constela app has four main sections:
1. Version
{
"version": "1.0"
}The version field specifies which version of the Constela DSL you're using. This ensures compatibility as the language evolves.
2. State
{
"state": {
"count": { "type": "number", "initial": 0 }
}
}State defines the reactive data in your application. Each state field has:
- A name (the key, e.g.,
"count") - A type (
"number","string", or"list") - An initial value
Note
State is automatically reactive. When state changes, the view updates automatically.
3. Actions
{
"actions": [
{
"name": "increment",
"steps": [
{ "do": "update", "target": "count", "operation": "increment" }
]
}
]
}Actions define how state changes. Each action has:
- A name field to identify the action
- An array of steps that execute sequentially
The do field specifies the step type:
"set"- Set a state value directly"update"- Modify state with an operation (increment, decrement, push, pop, remove)"fetch"- Make HTTP requests
4. View
{
"view": {
"kind": "element",
"tag": "button",
"props": { "onClick": { "event": "click", "action": "increment" } },
"children": [...]
}
}View defines the UI structure. The kind field specifies the node type:
"element"- HTML element"text"- Text content"if"- Conditional rendering"each"- List iteration"component"- Reusable component"slot"- Placeholder for children
Running the App
Save the JSON above as src/routes/counter.json in your Constela project, then start the development server:
npm run devOpen http://localhost:3000/counter to see your counter app in action.
Tip
The development server provides hot reloading and helpful error messages when your JSON has issues.
What's Next?
Now that you understand the basics, explore:
- State & Expressions - Deep dive into state management
- Actions & Events - Handle user interactions
- Components - Create reusable UI pieces