@stewie-js/core
v0.2.0
Published
Reactivity primitives, JSX runtime, context, and control flow for the Stewie framework
Maintainers
Readme
@stewie-js/core
❗ Work in progress.
Stewie is under active development and not yet stable. APIs may change between releases. Not recommended for production use yet.
The reactive foundation of Stewie. Provides fine-grained signals, computed values, effects, a proxy-based store, a JSX runtime, context, and built-in control flow components. No virtual DOM — reactive expressions subscribe directly to the DOM nodes they affect.
Part of the Stewie framework.
Install
pnpm add @stewie-js/coreReactivity
import { signal, computed, effect } from '@stewie-js/core'
const count = signal(0)
const doubled = computed(() => count() * 2)
effect(() => {
console.log('count:', count(), 'doubled:', doubled())
})
count.set(5) // logs: count: 5 doubled: 10
count.update(n => n + 1) // logs: count: 6 doubled: 12Store
A Proxy-based reactive object. Only the specific properties a component reads are tracked — changing an unrelated property causes no update.
import { store } from '@stewie-js/core'
const state = store({ user: { name: 'Alice' }, todos: [] as string[] })
state.user.name = 'Bob' // only components reading user.name update
state.todos.push('Buy milk') // only components reading todos updateJSX
Configure your tsconfig.json or vite.config.ts to use Stewie's JSX runtime:
{ "jsxImportSource": "@stewie-js/core" }Attribute expressions that are functions are wrapped in fine-grained effects. Static values are set once with no subscription.
import { signal } from '@stewie-js/core'
function Counter() {
const count = signal(0)
return (
<div>
<p>Count: {count}</p>
<button onClick={() => count.update(n => n + 1)}>+</button>
</div>
)
}Control Flow
import { Show, For, Switch, Match } from '@stewie-js/core'
// Conditional rendering
<Show when={() => loggedIn()}>
<Dashboard />
</Show>
// Keyed list rendering
<For each={() => items()}>
{(item) => <li>{item.name}</li>}
</For>
// Switch/Match
<Switch>
<Match when={() => status() === 'loading'}><Spinner /></Match>
<Match when={() => status() === 'error'}><Error /></Match>
<Match when={true}><Content /></Match>
</Switch>Context
import { createContext, inject } from '@stewie-js/core'
const ThemeContext = createContext('light')
function App() {
return (
<ThemeContext.Provider value="dark">
<Page />
</ThemeContext.Provider>
)
}
function Page() {
const theme = inject(ThemeContext) // 'dark'
return <div class={`theme-${theme}`}>...</div>
}API
| Export | Description |
|---|---|
| signal(value) | Reactive value — .set(), .update(), call to read |
| computed(fn) | Lazy derived value, memoized on strict equality |
| effect(fn) | Runs immediately, re-runs on dependency change, returns dispose |
| batch(fn) | Defer notifications until fn completes |
| untrack(fn) | Read reactive values without subscribing |
| store(obj) | Proxy-wrapped reactive object with path-level subscriptions |
| createContext(default) | Create a typed context token |
| inject(ctx) | Read the nearest provided context value |
| provide(ctx, value, fn) | Run fn with a context value provided |
| createRoot(fn) | Create an isolated reactive scope |
| mount(element, container) | Render a JSX element into a DOM node |
| hydrate(element, container) | Mount with server state from window.__STEWIE_STATE__ |
| Show | Conditional rendering component |
| For | Keyed list rendering component |
| Switch / Match | First-match conditional rendering |
| Portal | Render children into a different DOM node |
| ErrorBoundary | Catch rendering errors and show a fallback |
| ClientOnly | Render children on client only (empty string on server) |
