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

Published: (January 17, 2026 at 09:52 PM EST)
3 min read
Source: Dev.to

Source: Dev.to

What is Constela?

Constela is a compiler‑first UI language optimized for AI‑generated interfaces. Instead of writing JavaScript, you describe UI behavior in a constrained JSON DSL that is validated, analyzed, and compiled into minimal runtime code. With the latest release, Constela reaches feature parity with React/Next.js for most common use cases.

Timer Actions

delay

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

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 running timer by its ID.

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

Extended Event Data

Event handlers now have access to rich event data.

Event TypeAvailable Variables
Inputvalue, checked
Keyboardkey, code, ctrlKey, shiftKey, altKey, metaKey
MouseclientX, clientY, pageX, pageY, button
Touchtouches (array of clientX, clientY, pageX, pageY)
ScrollscrollTop, scrollLeft
File Inputfiles (array of name, size, type)

Keyboard shortcut example

{
  "onKeyDown": {
    "event": "keydown",
    "action": "handleShortcut",
    "payload": {
      "key": { "expr": "var", "name": "key" },
      "isCtrl": { "expr": "var", "name": "ctrlKey" }
    }
  }
}

File input example

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

Form Features

Programmatic focus control

// 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" }

Validation state (HTML5 Constraint Validation API)

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

Portal & Observer

Portal

Render content to a different location in the DOM (e.g., modals, 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": [ /* modal content */ ]
        }
      ]
    }
  ]
}

Target options

  • "body"document.body
  • "head"document.head
  • Any CSS selector (e.g., "#modal-root")

IntersectionObserver (infinite scroll)

{
  "kind": "element",
  "tag": "div",
  "ref": "sentinel",
  "props": {
    "onIntersect": {
      "event": "intersect",
      "action": "loadMoreItems",
      "options": {
        "threshold": 0.5,
        "rootMargin": "100px"
      }
    }
  }
}

Debounce & Throttle

Control how frequently an event handler runs.

Debounce (execute 300 ms after the last event)

{
  "onInput": {
    "event": "input",
    "action": "search",
    "debounce": 300
  }
}

Throttle (execute at most once per 100 ms)

{
  "onScroll": {
    "event": "scroll",
    "action": "trackScroll",
    "throttle": 100
  }
}

Common UI Patterns

PatternConstela Feature
Auto‑hiding notificationsdelay step
Real‑time data pollinginterval + clearTimer
Keyboard shortcutsExtended keyboard event data
File upload UIfiles event data
Form validationvalidity expression
Modal dialogsportal node
Infinite scrollintersect event
Search‑as‑you‑typedebounce option

Getting 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

Resources

Back to Blog

Related posts

Read more »

SEO + RAO + Access = Lead

SEO: Being Known - SEO is still relevant; it’s less about driving clicks and more about establishing your website as: - A source of facts - A reference point -...

Humans, Machines, and Ratatouille 🐀

Introduction A pragmatic response to system complexity in AI systems For a long time, we designed digital products the way many people imagine a kitchen works:...