| Crates.io | reaktiv |
| lib.rs | reaktiv |
| version | 0.1.1 |
| created_at | 2025-12-19 16:40:42.065999+00 |
| updated_at | 2025-12-19 17:04:32.251633+00 |
| description | A standalone, flexible fine-grained reactivity library |
| homepage | |
| repository | https://github.com/pascalkuthe/reaktiv |
| max_upload_size | |
| id | 1995090 |
| size | 235,720 |
A standalone, flexible fine-grained reactivity library designed to be unintrusive.
This crate aims to bring the benefits of fine-grained reactivity to contexts beyond traditional web UI. Reactivity is a useful pattern for managing dependencies and propagating changes - it doesn't have to be tied to a specific framework or runtime. At the same time I had performance concerns so this crate is also more optimized compared to other crates I found.
The particular use case that motivated this crate was building a subscription/hook system for a Python API (which also has a UI that uses the same reactivity system). When users change parameters through the API, downstream computations need to update automatically:
// Python bindings (via PyO3)
#[pyclass]
struct CircuitParameter {
value: f64,
signal: Signal, // Just 4 bytes of metadata
}
#[pymethods]
impl CircuitParameter {
fn set(&mut self, value: f64) {
self.value = value;
self.signal.emit(); // Triggers dependent computations
}
}
// Rust side: derived values auto-update
let power = Computed::new(|| {
voltage_signal.track_dependency();
current_signal.track_dependency();
voltage * current
});
Users get reactive behavior without knowing about signals - the API just works.
Fine-grained reactivity automatically tracks dependencies between data and computations, updating only what changed. When you read a reactive value inside an effect, the system records that dependency. When that value changes, only the effects that depend on it re-run.
This is distinct from coarse-grained approaches (like virtual DOM diffing) where changes trigger broad re-renders that must be narrowed down afterward.
reactive_graph (from Leptos)reactive_graph is a good implementation of fine-grained reactivity (and inspired this crate) but ultimately did not fit what I was looking for:
Less intrusive design: Leptos signals wrap and own your data (Signal<T>). I wanted signals to be just metadata - your values stay in your structs.
Less overhead: Reaktiv is carefully optimized and uses ~50% less memory compared to reactive_graph.
Arc<RwLock<T>> which adds overhead, especially for simple typesArc<> for the graph metadata itselfMore flexible intermediate computations: In reactive_graph, intermediate computations are a special case. If an effect simply writes to another signal, that's not the same as a computed (and can lead to duplicate updates). Reaktiv doesn't have such a special case - a single effect can update multiple signals without adverse effects.
Built to scale: Reaktiv has the concept of skippable effects for handling large-scale UIs.
use reaktiv::{Signal, Effect, Computed, Transaction, spawn_effect_loop};
struct Component {
value: f64,
signal: Signal,
}
impl Component {
fn set(&mut self, v: f64) {
self.value = v;
self.signal.emit();
}
}
spawn_effect_loop();
// Effects auto-track dependencies
let effect = Effect::new(|| {
component.signal.track_dependency();
println!("Value: {}", component.value);
});
// Batch multiple changes
Transaction::run(|| {
component.set(1.0);
component.set(2.0);
});
Signal<T>). These can be built on top of reaktiv but that's not its aimThe current MSRV is 1.85 (required for Rust 2024 edition).
This crate tracks the Rust version available on the previous Ubuntu LTS release (currently Ubuntu 22.04). Since Canonical updates Firefox and its Rust toolchain for security reasons, the MSRV follows what's available through Ubuntu's package ecosystem. MSRV bumps that comply with this policy are not considered breaking changes.