Reactivity
Learn Niral's fine-grained reactivity with state, derived values and props. Use signals, effects, batching and context without a virtual DOM.
On this page 4 sections
Niral compiles runes to fine-grained signals — no virtual DOM, no re-renders. When state changes, exactly the text nodes and attributes that depend on it update.
Runes
let count = $state(0) // reactive state
let double = $derived(count * 2) // recomputes when count changes
let { title, limit = 10 } = $props // component props (with defaults)
Assignments just work — count++, count += n, items = [...items, x] all compile to signal updates. Mutating an object inside a signal? Reassign it (items = [...items]) or produce a new row object so keyed lists repaint.
Effects and batching
import { effect, batch, untrack } from "niral/runtime"
effect(() => console.log("count is", count)) // re-runs on change
batch(() => { a = 1; b = 2 }) // one flush for many writes
untrack(() => read(count)) // read without subscribing
Context — no prop drilling
// provider (any ancestor)
setContext("theme", "dark")
// any descendant component, however deep
const theme = getContext("theme", "light")
Props are live views
A child never rebuilds when props change — each prop is a reactive view into the parent. Local $state, focus, and input contents all survive updates. Props are read-only; writing one throws with a hint.