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 names — class and for, not className / htmlFor. The DSL describes markup, so it stays framework-neutral (HTML and Vue also use class). The React adapter maps class → className and for → htmlFor 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, anddelete- strict-equality operators — write
==/!=, which export compiles to===/!== ++/--increment and decrement- comma sequences and tagged templates
- access to
constructor,__proto__, orprototype
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.