@llui/dom
v0.0.3
Published
LLui runtime — TEA component model, mount, scope tree, bindings, structural primitives, element helpers
Maintainers
Readme
@llui/dom
Runtime for the LLui web framework — The Elm Architecture with compile-time bitmask optimization.
No virtual DOM. view() runs once at mount, building real DOM nodes with reactive bindings that update surgically when state changes.
Install
pnpm add @llui/domQuick Start
import { component, mountApp, div, button } from '@llui/dom'
type State = { count: number }
type Msg = { type: 'inc' } | { type: 'dec' }
const Counter = component<State, Msg, never>({
name: 'Counter',
init: () => [{ count: 0 }, []],
update: (state, msg) => {
switch (msg.type) {
case 'inc':
return [{ ...state, count: state.count + 1 }, []]
case 'dec':
return [{ ...state, count: state.count - 1 }, []]
}
},
view: ({ send, text }) => [
button({ onClick: () => send({ type: 'dec' }) }, [text('-')]),
text((s) => String(s.count)),
button({ onClick: () => send({ type: 'inc' }) }, [text('+')]),
],
})
mountApp(document.getElementById('app')!, Counter)View<S, M> — the helper bundle
view receives a single View<S, M> bag. Destructure what you need — send plus any state-bound helpers. TypeScript infers S from the component definition, so no per-call generics:
view: ({ send, text, show, each, branch, memo }) => [
text(s => s.label), // s is State — inferred
...show({ when: s => s.visible, render: () => [...] }),
...each({ items: s => s.items, key: i => i.id, render: ({ item }) => [...] }),
]Element helpers (div, button, span, etc.) stay as imports — they're stateless and don't need the S binding.
API
Core
| Export | Purpose |
| --------------------- | ------------------------------------------------- |
| component(def) | Create a component definition |
| mountApp(el, def) | Mount a component to a DOM element |
| hydrateApp(el, def) | Hydrate server-rendered HTML |
| flush() | Synchronously flush all pending updates |
| createView(send) | Create a full View bundle (for tests/dynamic use) |
View Primitives
| Primitive | Purpose |
| ------------------------------ | --------------------------------------------- |
| text(accessor) | Reactive text node |
| show({ when, render }) | Conditional rendering |
| branch({ on, cases }) | Multi-case switching |
| each({ items, key, render }) | Keyed list rendering |
| portal({ target, render }) | Render into a different DOM location |
| child({ def, key, props }) | Full component boundary (Level 2 composition) |
| memo(accessor) | Memoized derived value |
| selector(field) | O(1) one-of-N selection binding |
| onMount(callback) | Lifecycle hook (runs once after mount) |
| errorBoundary(opts) | Catch render errors |
| foreign({ create, update }) | Integrate non-LLui libraries |
| slice(h, selector) | View over a sub-slice of state |
Composition
| Export | Purpose |
| ----------------------------------------- | -------------------------------- |
| mergeHandlers(...handlers) | Combine multiple update handlers |
| sliceHandler({ get, set, narrow, sub }) | Route messages to a state slice |
Context
| Export | Purpose |
| ---------------------------------- | ------------------------ |
| createContext(defaultValue) | Create a context |
| provide(ctx, accessor, children) | Provide value to subtree |
| useContext(ctx) | Read context value |
Element Helpers
50+ typed element constructors: div, span, button, input, a, h1-h6, table, tr, td, ul, li, img, form, label, select, textarea, canvas, video, nav, header, footer, section, article, p, pre, code, and more.
SSR
| Export | Purpose |
| --------------------- | ----------------------------------------------- |
| renderToString(def) | Render component to HTML string |
| initSsrDom() | Initialize jsdom for SSR (from @llui/dom/ssr) |
Sub-path Exports
import { installDevTools } from '@llui/dom/devtools' // dev-only, tree-shaken
import { initSsrDom } from '@llui/dom/ssr' // server-only
import { replaceComponent } from '@llui/dom/hmr' // HMR supportPerformance
Competitive with Solid and Svelte on js-framework-benchmark. 5.8 KB gzipped.
License
MIT
