NTML is a reactive client-side single page application (SPA) renderer written in Nim. It provides a lightweight signal and effect system, and a JSX-like DSL for composing DOM nodes with reactive updates.
- Signals: reactive primitives for state management.
- Derived Signals: automatically compute values from other signals.
- Effects: side effects that run in response to signal changes.
- DOM Helpers: simple wrappers for element creation and updates.
- Control Flow: templates for
if,case, and loops inside the DSL. - Component Props: composable component definitions with inheritance support.
- Routing: simple and intuitive routing with
navigate(). - Styled Components: reactive
styledmacro keeps components clean and organized. - Form Bindings: built-in
bindValue/bindCheckedwire signals to form inputs for two-way updates. - Lifecycle Cleanup: automatic teardown releases subscriptions, event listeners, and styled classes when nodes unmount.
- Signal Operators: rich overloads let you compose comparisons and boolean logic directly on
Signals. - Reactive CSS Vars: the
styleVarshelper keeps CSS custom properties in step with live signal data. - Keyed List Rendering: efficiently reconciles list updates by reusing existing DOM nodes, preserving element identity and minimizing re-renders.
var count: Signal[int] = signal(0)
let doubled: Signal[string] = derived(count, proc (x: int): string = $(x*2))
let component: Node =
d(id="container"):
"Count: "; count; br();
"Doubled: "; doubled; br(); br();
button(
type="button",
onClick = proc (e: Event) =
count.set(count() + 1)
):
"Increment"Runnable examples are available in the examples directory. First start a server at the project root:
npx serve --single .Add an index.html file at the project root:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Run Nim-Generated JS</title>
</head>
<body>
<script src="/index.js"></script>
</body>
</html>Hello world:
nim js --out:index.js examples/helloWorld.nim- Hello World (
examples/helloWorld.nim): smallest possible component render, useful for sanity-checking your toolchain. - Todos (
examples/todos.nim): reactive list management withmountChildFor, derived filters, two-way<input>bindings, and dynamic styling. - Forms (
examples/forms.nim): showcases nested signals, validation hints, andbindValue/bindCheckedhelpers. - Routing (
examples/router.nim): leveragesnavigate()and route signals to orchestrate multipage flows. - Styling (
examples/styled.nim): demonstrates thestyledmacro, scoped CSS hashing, and reactivestyleVars. - Keyed Diffs (
examples/keyedDiffs.nim): showcases keyed list rendering, highlighting when entries patch versus remount. - Effects (
examples/effect.nim): theeffectexplores the effect API, including cleanup, auto-runners, and delayed updates. - Overloads (
examples/overloads.nim): comprehensive showcase of signal operator overloads in one live dashboard.
This project is still experimental. As such, it is not currently in a state that is deemed
ready for a production environment. A roadmap can be found in ROADMAP.md.
