valtio-reactive makes Valtio a reactive library
npm install valtio-reactive



Reactive primitives for valtio — adds, computed, effect, and batch to enable fine-grained reactivity outside of React.
valtio's reactive capabilities are primarily designed for React via useSnapshot and only has limited support for computed values. valtio-reactive was made to fill those gaps while keeping valtio lean and fast.
- Run side effects when specific properties change (not just any change)
- Create derived/computed state that automatically updates
- Batch multiple updates into a single reaction
See the original discussion for more context.
``bash`
npm install valtio valtio-reactive
This runs the first function (fn) immediately and re-runs it whenever any of the properties that are accessed in that function change. Only the properties are actually read during execution are tracked — changes to unread properties won't trigger re-runs. It returns a dispose function that will run the cleanup function when called.
`ts
import { proxy } from 'valtio/vanilla';
import { effect } from 'valtio-reactive';
const state = proxy({
count: 0,
unrelated: 'hello'
user: {
settings: {
theme: 'light' //
},
name: 'Bob'
},
})
const dispose = effect(
() => {
console.log('count is: ', state.count)
console.log('theme is: ', state.user.settings.theme)
},
() => {
// optional cleanup function
console.log('cleaning up')
}
)
// immediately logs:
// "count is: 0"
// "theme is: light'
state.count++
// logs:
// "count is: 1"
// "theme is: light"
state.unrelated = 'world' // nothing happens when this property is changed because it wasn't accessed
state.user.name = 'Robert' // nothing happens
state.user.settings.theme = 'dark'
// logs:
// "count is: 1"
// "theme is: dark"
dispose()
// logs "cleaning up"
`
---
Batches multiple state changes so that effects only react once after all changes complete. Returns the value returned by fn.
`ts
import { proxy } from 'valtio/vanilla';
import { batch, effect } from 'valtio-reactive';
const state = proxy({ count: 0 });
effect(() => {
console.log('count:', state.count);
});
// Logs: "count: 0"
batch(() => {
state.count++;
state.count++;
state.count++;
});
// Logs: "count: 3" (only once, not three times)
`
---
Creates a proxy object with computed/derived properties. Each property is defined as a getter function that automatically re-runs when its dependencies change.
`ts
import { proxy } from 'valtio/vanilla';
import { computed } from 'valtio-reactive';
const state = proxy({ count: 1 });
const derived = computed({
double: () => state.count * 2,
quadruple: () => state.count * 4,
});
console.log(derived.double); // 2
console.log(derived.quadruple); // 4
state.count = 5;
console.log(derived.double); // 10
console.log(derived.quadruple); // 20
`
The returned object is itself a valtio proxy, so you can use it with effect, useSnapshot, or any other valtio utility.
---
While these primitives are framework-agnostic, they integrate seamlessly with valtio's React bindings:
`tsx
import { proxy, useSnapshot } from 'valtio';
import { effect, computed } from 'valtio-reactive';
const state = proxy({ count: 0 });
// Computed values work with useSnapshot
const derived = computed({
double: () => state.count * 2,
});
// Side effects outside of React
effect(() => {
console.log('Count changed:', state.count);
});
function Counter() {
const snap = useSnapshot(state);
const derivedSnap = useSnapshot(derived);
return (
Count: {snap.count}
Double: {derivedSnap.double}
---
TypeScript
All exports are fully typed. The
computed function infers types from your getter functions:`ts
const state = proxy({ count: 1, name: 'test' });const derived = computed({
double: () => state.count * 2, // inferred as number
message: () =>
Hello ${state.name}, // inferred as string
});derived.double; // number
derived.message; // string
``MIT