@bitstillery/mithril
v3.4.1
Published
Mithril + Signals, Store and SSR
Downloads
610
Readme
Mithril Bitstillery
Mithril Bitstillery extends Mithril with integrated state management, SSR hydration, watchers, and a signal/proxy store. Drop-in compatible with Mithril v2.x.
bun add @bitstillery/mithrilStrengths
Mithril Bitstillery focuses on state management, SSR, watchers, and developer experience around its signal/proxy store:
| Feature | Description |
| ---------------- | ------------------------------------------------------------------- |
| Proxy State | Reactive objects with nested objects, arrays, computeds |
| State Management | state() + Store for persistence (localStorage, session) |
| SSR | Full server-side rendering with state serialization + hydration |
| Watchers | watch() for observing signal changes; effect() for side effects |
| DX | Automatic dependency tracking, no manual redraw for signals |
Docs: mithril.garage44.org
Signals
Zero-dependency reactive primitives with automatic dependency tracking:
import {signal, computed, effect} from '@bitstillery/mithril'
const count = signal(0)
const doubled = computed(() => count() * 2)
effect(() => console.log(`${count()} × 2 = ${doubled()}`))
count(5) // Logs: 5 × 2 = 10Proxy State
state() creates reactive objects. Components track which properties they read and only re-render when those change. Function properties become computed values—they re-evaluate when their dependencies change.
import m, {state, MithrilComponent} from '@bitstillery/mithril'
const $s = state({count: 0, todos: [], totalTodos: () => $s.todos.length}, 'app')
class Counter extends MithrilComponent {
view() {
return (
<div>
<p>
{$s.count} / {$s.totalTodos}
</p>
<button onclick={() => $s.count++}>+</button>
</div>
)
}
}
m.mount(document.body, Counter)- Computed properties: Any function in state (e.g.
totalTodos: () => $s.todos.length) is a computed—read it like a normal property, it updates when dependencies change. $prefix: Use$s.$countfor the raw signal (e.g. forwatch()).- The second argument is a name used for SSR serialization.
Watchers
watch() observes signal changes:
import {state, watch} from '@bitstillery/mithril'
const $s = state({count: 0}, 'app')
const unwatch = watch($s.$count, (newVal, oldVal) => console.log(`${oldVal} → ${newVal}`))
$s.count++ // triggers callback
unwatch() // stop observingSSR Hydration
// Server
const {html, state} = await m.renderToString(App)
// Inject: <script id="__SSR_STATE__">${JSON.stringify(state)}</script>
// Client
import {deserializeAllStates} from '@bitstillery/mithril'
const el = document.getElementById('__SSR_STATE__')
if (el?.textContent) deserializeAllStates(JSON.parse(el.textContent))
m.mount(root, App)Persistent Store
Store wraps state() with localStorage/sessionStorage. Define a blueprint with defaults and which keys persist:
import {Store} from '@bitstillery/mithril'
const store = new Store<{user: {name: string}; preferences: Record<string, any>}>()
store.blueprint(
{user: {name: ''}, preferences: {}},
{user: {name: ''}, preferences: {}}, // Keys here persist to storage
)
store.load({user: {name: 'John'}, preferences: {theme: 'dark'}})
store.state.user.name = 'Jane' // Auto-savesExamples
examples/ssr/— Server-side rendering with hydrationexamples/state/— Signals, state, and Store patterns
Development
bun install
bun testLicense
MIT — see LICENSE.
Credits
Originally created by Leo Horie. See the Mithril.js contributors for the many people who made Mithril what it is.
