The DSL Language

The nuforge DSL is a small, declarative language for describing pages. A program is a list of globals and components; nuforge parses it into a reactive AST. This page walks through the complete syntax.

Globals

Use val at the top level to declare shared values. They can hold any allowed expression, including objects:

val brand = "nuforge";
val theme = { primary: "#0066DE" };

Components

A component has props with defaults in parentheses, local state declared with val in braces, and a template after =>:

component App(title = "Untitled") {
  val count = 0;
  val items = ["a", "b"];
} => (
  <div>
    <h1>{title}</h1>
  </div>
)

The props block, the state block, and the template are each optional, but a component always declares its template after the => arrow.

Text and expressions

Inside a template, curly braces hold expressions. A string literal like {"a static string"} renders static text, while {expr} evaluates a dynamic expression such as {count}. Template literals are supported — write them in backticks inside an expression:

<p>{`Hello, ${title}`}</p>

Attributes

Attributes take expressions in braces. Use a string literal for a constant, or any expression for a dynamic value:

<div class={"page"} id={someId} data-count={count} />

You write HTML attribute namesclass and for, not className / htmlFor. The DSL describes markup, so it stays framework-neutral (HTML and Vue also use class). The React adapter maps classclassName and forhtmlFor at render time, and code export does the same — so class={"page"} becomes className="page" in the generated React.

Inline styles

A <style> (or <script>) tag keeps its content as raw text, so you can write real CSS — use a backtick string for multi-line rules and combinators:

<style>{`
  .card { padding: 1rem; border-radius: 8px; }
  .card > .title { font-weight: 700; }
`}</style>

For most styling prefer utility classes (class={"..."}); reach for <style> when you need actual CSS rules. To scope styles to the editor canvas only, use the srcDoc / baseCss props of IframeCanvas (see Editor SDK).

Directives

Directives are special attributes that control rendering:

  • @if={cond} conditionally renders the element.
  • @each={item in list} or @each={(item, index) in list} repeats the element for each entry.
  • @classList={{ active: count > 0 }} toggles classes based on a condition.
<li @each={(item, index) in items} @classList={{ first: index == 0 }}>{item}</li>

Two-way binding

The value:={...} binding keeps an input controlled and writes the user's edits back to state:

<input value:={name} />

Events

Event handlers are arrow functions. They can write to state directly:

<button onClick={() => count = count + 1}>{count}</button>

Composition

A tag whose name starts with an uppercase letter is a component instance. Pass props as attributes:

<Card title={"Hi"} />

Externals

Identifiers prefixed with $ refer to host-provided values. Use $state for external state, $func(...) to call a host function, and <$Component/> to render an external component:

<$Component label={$state} onPick={() => $func("pick")} />

See the externals page for how to register these from the host.

What's allowed

For safety, the parser only accepts a known set of expression nodes: literals, identifiers, member access, arrays, objects, binary / logical / unary operators, conditional (ternary) expressions, function calls, arrow functions (for events), and template literals.

The parser REJECTS anything outside that allow-list, including:

  • new, function, this, typeof, and delete
  • strict-equality operators — write == / !=, which export compiles to === / !==
  • ++ / -- increment and decrement
  • comma sequences and tagged templates
  • access to constructor, __proto__, or prototype

See the security page for the full guarantees behind these rules.

Authoring: validation & the code editor

Parser.diagnose(source) validates DSL without throwing — it returns a list of structured diagnostics (empty when valid), each with a message, a code, and a source offset range plus line/column:

import { Parser } from '@nuforge/parser';

Parser.diagnose(`component App() {} => (<div>{new Date()}</div>)`);
// [{ severity: 'error', code: 'forbidden-syntax',
//    message: 'Syntax "NewExpression" is not allowed',
//    from, to, start: { line, column }, end }]

The @nuforge/react-code-editor component (CodeEditor) builds on this: it ships nuforge DSL autocompletion (keywords, directives, HTML tags, and your program's component/state/prop/global and $external names) and live error squiggles driven by diagnose.

Next steps

  • Runtime — evaluate the AST into a View tree with a Frame.
  • Externals — register host state, functions, and components.