pream



Signals-first Gleam bindings for Preact with
@preact/signals integration.
Re-rendering is driven by signals, not component state.
Philosophy
pream is signals-first. Components don't hold local state — they read from
signals and re-render when signals change. This means:
- No
useState / useReducer — use signal.new and signal.set instead - No manual dependency arrays for signal effects — use
use_signal_effect memo skips VNode building when props haven't changed across re-renders
Install
gleam add pream
Requires Preact and @preact/signals as npm dependencies:
npm install preact @preact/signals
Quick start
import pream
import pream/hooks
import pream/signal
import pream/vnode
pub fn main() {
let count = signal.new(0)
let app =
vnode.div()
|> vnode.children([
vnode.text("Clicked "),
vnode.reactive_text(signal.map(count, fn(n) { "clicked " <> n })),
vnode.text(" times"),
vnode.button()
|> vnode.on("click", fn() { signal.set(count, signal.value(count) + 1) })
|> vnode.child(vnode.text("Increment")),
])
pream.to_preact(app)
}
Examples
Counter with signal
import pream
import pream/signal
import pream/vnode
pub fn counter() {
let count = signal.new(0)
vnode.div()
|> vnode.child(vnode.reactive_text(signal.map(count, fn(n) {
"Count: " <> int.to_string(n)
})))
|> vnode.child(
vnode.button()
|> vnode.on("click", fn() {
signal.set(count, signal.value(count) + 1)
})
|> vnode.child(vnode.text("Increment")),
)
}
Conditional rendering
import pream/vnode
import pream/signal
pub fn visibility_toggle() {
let visible = signal.new(True)
vnode.div()
|> vnode.child(vnode.when_signal(visible, fn() {
vnode.text("Hello, world!")
}))
|> vnode.child(
vnode.button()
|> vnode.on("click", fn() {
signal.set(visible, !signal.value(visible))
})
|> vnode.child(vnode.text("Toggle")),
)
}
Memoized component
import pream
import pream/hooks
import pream/vnode
pub fn expensive_list() {
let comp = pream.component(fn(items: List(String)) {
vnode.ul()
|> vnode.children(
list.map(items, fn(item) {
vnode.element(
vnode.li() |> vnode.child(vnode.text(item))
)
}),
)
})
hooks.memo(comp)
}
Using useEffect
import pream/hooks
import pream/dom
pub fn timer_component() {
let count = hooks.use_ref(0)
hooks.use_effect(fn() {
let id = setInterval(fn() { count.current = count.current + 1 }, 1000)
fn() { clearInterval(id) }
}, [])
vnode.div() |> vnode.child(vnode.text("Timer running"))
}
Features
- Signals-first — re-rendering driven by signals, not component state
- Shorthand constructors —
vnode.div(), vnode.span(), vnode.button(),
etc. - Pipe-friendly modifiers —
prop, on, class, id, disabled, ... - Fine-grained signals —
signal.new, signal.computed, signal.effect - Component hooks —
use_signal, use_signal_effect, use_computed - Preact hooks —
use_effect, use_memo, use_callback, use_ref,
use_id, ... - Component memo —
memo, memo_custom for skipping unnecessary VNode
builds - QoL hooks —
use_mount, use_unmount - Conditional helpers —
vnode.when, vnode.unless, vnode.when_some,
vnode.when_signal, vnode.map_signal - Result/Option-aware —
pream.unwrap and pream.unwrap_option silently
degrade into empty nodes on Error/None
Hooks
All hooks are available from import pream/hooks.
Signal hooks
| Hook | Description |
|---|
use_signal(initial) | Create a reactive signal scoped to a component |
use_signal_effect(run) | Run a reactive effect on signal changes |
use_computed(fn) | Create a computed signal scoped to a component |
Effect hooks
| Hook | Description |
|---|
use_effect(run, deps) | Side effect after render (no cleanup) |
use_effect_cleanup(run, deps) | Side effect with cleanup function |
use_layout_effect(run, deps) | Synchronous effect after DOM mutations |
use_layout_effect_cleanup(run, deps) | Synchronous effect with cleanup |
Memoization hooks
| Hook | Description |
|---|
use_memo(compute, deps) | Memoize an expensive computation |
use_callback(callback, deps) | Memoize a callback function |
Ref hooks
| Hook | Description |
|---|
use_ref(initial) | Create a mutable reference object |
use_imperative_handle(ref, create, deps) | Customize ref handle |
Misc hooks
| Hook | Description |
|---|
use_id() | Unique ID for accessibility attributes |
use_debug_value(value) | Custom devtools label |
Component memo
| Hook | Description |
|---|
memo(comp) | Wrap a component with shallow props comparison |
memo_custom(comp, compare) | Wrap with custom comparison function |
QoL hooks
| Hook | Description |
|---|
use_mount(fn) | Run once on mount |
use_unmount(fn) | Run once on unmount |
Documentation
License
MIT © 2026 soulsam480