What is Constela?
Constela is a compiler-first UI language optimized for AI-generated UIs. Instead of writing JavaScript, you describe UI behavior in a constrained JSON DSL that's validated, analyzed, and compiled into minimal runtime code.
With , we've added features that bring Constela to feature parity with React/Next.js for most common use cases.
New Features Overview
- Timer Actions - delay, interval, clearTimer
- Extended Event Data - keyboard, mouse, touch, scroll, files
- Form Features - focus control, validation state
- Portal & Observer - portal node, IntersectionObserver, debounce/throttle
1. Timer Actions
delay - Delayed Execution
Execute steps after a specified delay.
{
"name": "showNotification",
"steps": [
{ "do": "set", "target": "notification", "value": { "expr": "lit", "value": "Saved!" } },
{
"do": "delay",
"ms": { "expr": "lit", "value": 3000 },
"then": [
{ "do": "set", "target": "notification", "value": { "expr": "lit", "value": "" } }
]
}
]
}
interval - Periodic Execution
Execute an action repeatedly at a specified interval. The timer ID is stored in result.
{
"name": "startPolling",
"steps": [
{
"do": "interval",
"ms": { "expr": "lit", "value": 5000 },
"action": "fetchLatestData",
"result": "pollingTimerId"
}
]
}
clearTimer - Stop a Timer
Stop a running timer by its ID.
{
"name": "stopPolling",
"steps": [
{ "do": "clearTimer", "target": { "expr": "state", "name": "pollingTimerId" } }
]
}
2. Extended Event Data
Event handlers now have access to rich event data.
| Event Type | Available Variables |
|---|---|
| Input |
value, checked
|
| Keyboard |
key, code, ctrlKey, shiftKey, altKey, metaKey
|
| Mouse |
clientX, clientY, pageX, pageY, button
|
| Touch |
touches (array: clientX, clientY, pageX, pageY) |
| Scroll |
scrollTop, scrollLeft
|
| File Input |
files (array: name, size, type) |
Example: Keyboard Shortcuts
{
"onKeyDown": {
"event": "keydown",
"action": "handleShortcut",
"payload": {
"key": { "expr": "var", "name": "key" },
"isCtrl": { "expr": "var", "name": "ctrlKey" }
}
}
}
Example: File Upload
{
"kind": "element",
"tag": "input",
"props": {
"type": { "expr": "lit", "value": "file" },
"multiple": { "expr": "lit", "value": true },
"onChange": {
"event": "change",
"action": "handleFiles",
"payload": { "expr": "var", "name": "files" }
}
}
}
3. Form Features
Focus Control
Programmatically control focus on form elements.
// Focus an element
{ "do": "focus", "target": { "expr": "ref", "name": "emailInput" }, "operation": "focus" }
// Select text
{ "do": "focus", "target": { "expr": "ref", "name": "codeInput" }, "operation": "select" }
// Blur (unfocus)
{ "do": "focus", "target": { "expr": "ref", "name": "searchInput" }, "operation": "blur" }
validity Expression - Form Validation State
Access HTML5 Constraint Validation API through expressions.
{
"kind": "element",
"tag": "input",
"ref": "emailInput",
"props": {
"type": { "expr": "lit", "value": "email" },
"required": { "expr": "lit", "value": true }
}
}
// Display validation errors
{
"kind": "if",
"condition": {
"expr": "not",
"operand": { "expr": "validity", "ref": "emailInput", "property": "valid" }
},
"then": {
"kind": "text",
"value": { "expr": "validity", "ref": "emailInput", "property": "message" }
}
}
Available properties:
-
valid- Overall validity -
valueMissing- Required field is empty -
typeMismatch- Type doesn't match (email, url, etc.) -
patternMismatch- Pattern doesn't match -
tooLong/tooShort- Length constraint violation -
rangeUnderflow/rangeOverflow- Range constraint violation -
message- Browser's validation message
4. Portal & Observer
Portal - Render Outside Component Tree
Render content to a different location in the DOM, perfect for modals and tooltips.
{
"kind": "portal",
"target": "body",
"children": [
{
"kind": "element",
"tag": "div",
"props": { "className": { "expr": "lit", "value": "modal-overlay" } },
"children": [
{
"kind": "element",
"tag": "div",
"props": { "className": { "expr": "lit", "value": "modal-content" } },
"children": [...]
}
]
}
]
}
Target options:
-
"body"- document.body -
"head"- document.head - CSS selector (e.g.,
"#modal-root")
IntersectionObserver - Visibility Detection
Implement infinite scroll and lazy loading with ease.
{
"kind": "element",
"tag": "div",
"ref": "sentinel",
"props": {
"onIntersect": {
"event": "intersect",
"action": "loadMoreItems",
"options": {
"threshold": 0.5,
"rootMargin": "100px"
}
}
}
}
debounce / throttle
Control event handler execution frequency.
// Debounce: Execute 300ms after the last event
{
"onInput": {
"event": "input",
"action": "search",
"debounce": 300
}
}
// Throttle: Execute at most once per 100ms
{
"onScroll": {
"event": "scroll",
"action": "trackScroll",
"throttle": 100
}
}
Use Cases Enabled
These features enable common UI patterns:
| Pattern | Constela Feature |
|---|---|
| Auto-hiding notifications |
delay step |
| Real-time data polling |
interval + clearTimer
|
| Keyboard shortcuts | Extended keyboard event data |
| File upload UI |
files event data |
| Form validation |
validity expression |
| Modal dialogs |
portal node |
| Infinite scroll |
intersect event |
| Search-as-you-type |
debounce option |
Why Constela?
Constela is designed for:
- AI-generated UI - Structured JSON is easier for LLMs to generate correctly
- Compile-time validation - Catch errors before runtime
- Deterministic behavior - No arbitrary JavaScript, predictable state transitions
- Smaller bundles - Compile away what you don't need
Get Started
npm install @constela/start
mkdir -p src/routes
echo '{"version":"1.0","state":{},"actions":[],"view":{"kind":"element","tag":"div","children":[{"kind":"text","value":{"expr":"lit","value":"Hello Constela!"}}]}}' > src/routes/index.json
npx constela dev
Top comments (1)
This is genuinely interesting work — not because it’s “React but different,” but because it attacks a problem most UI frameworks quietly ignore.
What stands out to me is that Constela isn’t trying to replace JavaScript expressiveness, it’s trying to constrain it intentionally. That’s a big philosophical shift. Most UI bugs don’t come from lack of power — they come from too much freedom with no guardrails. A compiler-first JSON DSL flips that tradeoff in a way that actually makes sense for AI-generated code.
The timer primitives, portals, observers, and form validity support are especially telling. Those are usually the spots where “toy DSLs” fall apart. Here, you’re not hand-waving them — you’re encoding them as first-class, analyzable concepts. That’s how React won: not because JSX is magic, but because effects, state, and lifecycles became explicit mental models. Constela feels like it’s doing the same thing, just one layer lower and more formal.
The AI angle also feels real, not marketing. LLMs are objectively better at producing constrained, schema-valid structures than open-ended JS with side effects. Giving them a language where behavior is declarative, validated, and compiled instead of trusted is exactly the direction things should go if we care about determinism and safety.
To me this reads less like “React parity” and more like UI as a verifiable system, which is a much stronger claim. Especially when you start thinking about auditability, replayable state transitions, and predictable runtime behavior — things that become non-negotiable once AI is in the loop.
Curious to see how far this goes once people start building real apps with it. This feels like one of those ideas that looks niche at first and then suddenly explains why the old way was fragile all along.