Skip to content

bitstillery/mithril

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4,002 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

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/mithril

Strengths

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 = 10

Proxy 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.$count for the raw signal (e.g. for watch()).
  • 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 observing

SSR 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-saves

Examples

Development

bun install
bun test

License

MIT — see LICENSE.

Credits

Originally created by Leo Horie. See the Mithril.js contributors for the many people who made Mithril what it is.

Releases

No releases published

Packages

 
 
 

Contributors

Languages

  • TypeScript 98.7%
  • Other 1.3%