DEV Community

Yuuichi Eguchi
Yuuichi Eguchi

Posted on

Constela : Achieving React/Next.js-Level Expressiveness with Timers, Forms, Portals & More

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

  1. Timer Actions - delay, interval, clearTimer
  2. Extended Event Data - keyboard, mouse, touch, scroll, files
  3. Form Features - focus control, validation state
  4. 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": "" } }
      ]
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

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"
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

clearTimer - Stop a Timer

Stop a running timer by its ID.

{
  "name": "stopPolling",
  "steps": [
    { "do": "clearTimer", "target": { "expr": "state", "name": "pollingTimerId" } }
  ]
}
Enter fullscreen mode Exit fullscreen mode

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" }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

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" }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

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" }
Enter fullscreen mode Exit fullscreen mode

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 }
  }
}
Enter fullscreen mode Exit fullscreen mode
// 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" }
  }
}
Enter fullscreen mode Exit fullscreen mode

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": [...]
        }
      ]
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

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"
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

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
  }
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Links

Top comments (1)

Collapse
 
peacebinflow profile image
PEACEBINFLOW

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.