No virtual DOM. No diffing. Every update is O(1).
A signal changes, and exactly one text node updates. Nothing else runs.
$ npm create cellui@latest
import { signal, computed, view, mount } from '@cmj/cellui' const App = () => { const count = signal(0) const doubled = computed(() => count.value * 2) return view` <h1>${count} × 2 = ${doubled}</h1> <button onclick="${() => count.value++}">+</button> ` } mount('#app', () => App())
Most frameworks diff a virtual tree to find changes. CellUI skips the middleman.
A signal wraps a value and maintains a subscriber list via WeakRef.
const count = signal(0)
The view engine maps each interpolation to an exact text node or attribute.
view`<h1>${count}</h1>`
When the signal changes, only that one DOM node updates. No tree walk. No diff.
count.value++ // one node
Derived state with automatic dependency tracking. No useMemo, no dependency arrays.
URL-synced routing with pattern matching, params, and browser history.
Render modals and tooltips outside the parent DOM tree with automatic cleanup.
Server-render HTML with serverView(), hydrate on the client. This site does it.
CSS enter/leave animations with automatic teardown timing.
Built-in render() with getByText and getBySelector. No extra library needed.
When a DOM node is garbage collected, its signal subscription dies automatically.
Shield catches render errors with fallback UI. Nine standardized error codes.
CellUI vs hand-written vanilla JS. Lower is better.
ISC licensed. 5KB. Zero dependencies.