High-performance, fine-grained reactive state management system.
- π Zero Dependencies - Lightweight and standalone
- β‘ Fine-Grained Reactivity - Only update what changes
- π Diamond Dependency Resolution - Handles complex dependency graphs efficiently
- π Batched Updates - Automatic batching for optimal performance
- π§Ή Automatic Cleanup - Memory-safe with automatic resource disposal
- π¦ Deep Reactive Stores - Proxy-based reactivity for nested objects
- π TypeScript First - Full TypeScript support with excellent type inference
- π― Predictable - Synchronous and glitch-free updates
- ποΈ High Performance - Optimized with caching, pooling, and smart invalidation
npm install vibrancy
# or
yarn add vibrancy
# or
pnpm add vibrancyimport { signal, computed, effect, batch } from 'vibrancy';
// Create reactive signals
const count = signal(0);
const multiplier = signal(2);
// Create computed values that auto-update
const doubled = computed(() => count() * 2);
const result = computed(() => count() * multiplier());
// Create side effects that run when dependencies change
effect(() => {
console.log(`Count: ${count()}, Result: ${result()}`);
});
// Update signals
count.set(5); // Logs: Count: 5, Result: 10
// Batch updates for efficiency
batch(() => {
count.set(10);
multiplier.set(3);
}); // Logs once: Count: 10, Result: 30Signals are the basic reactive primitive. They hold a value and notify dependents when it changes.
const value = signal(initialValue);
// Read the value
console.log(value()); // current value
// Update the value
value.set(newValue);
// Update with a function
value.update(prev => prev + 1);
// Subscribe to changes
const unsubscribe = value.subscribe(newValue => {
console.log('Value changed:', newValue);
});
// Peek at value without tracking dependencies
const currentValue = value.peek();Computed values derive from other reactive values and update automatically.
const computed = computed(() => signal1() + signal2());Effects run side effects when their dependencies change.
const dispose = effect(() => {
console.log('Value changed:', signal());
});
// Clean up when done
dispose();Stores provide reactive objects with nested reactivity.
const store = store({
user: { name: 'John', age: 30 },
settings: { theme: 'dark' }
});
// Access nested properties reactively
effect(() => {
console.log(`${store.user.name} uses ${store.settings.theme} theme`);
});
// Update nested values
store.user.name = 'Jane';
store.settings.theme = 'light';Resources handle async data fetching with loading states.
const userId = signal(1);
const user = resource(async () => {
const response = await fetch(`/api/user/${userId()}`);
return response.json();
});
// Access loading state, error, and data
effect(() => {
if (user.loading()) {
console.log('Loading user...');
} else if (user.error()) {
console.log('Error:', user.error().message);
} else {
console.log('User:', user());
}
});
// Change userId triggers automatic refetch
userId.set(2);signal<T>(value: T, options?: SignalOptions)- Create a reactive signalcomputed<T>(fn: () => T, options?: ComputedOptions)- Create a computed valueeffect(fn: () => void, options?: EffectOptions)- Create a side effectbatch(fn: () => void)- Batch multiple updatesuntrack(fn: () => T)- Read without tracking
store<T>(initial: T, options?: StoreOptions)- Create a reactive storeselector<T, R>(store: Store<T>, fn: (value: T) => R)- Create a store selectortransaction<T>(fn: () => T)- Batch store updates
resource<T>(fetcher: () => Promise<T>, options?: ResourceOptions)- Create an async resourceasyncComputed<T>(fn: () => Promise<T>, options?: AsyncComputedOptions)- Create async computed values
getOwner()- Get current reactive owneronCleanup(fn: () => void)- Register cleanup function
asyncComputed<T>(fn: () => Promise<T>)- Async computed valuesresolveDiamondDependencies()- Handle diamond dependency patternsStoreSubscriptionManager- Manage store subscriptionsStoreMiddlewareManager- Add middleware to stores
import { store, computed, effect } from 'vibrancy';
const todos = store({
items: [],
filter: 'all'
});
const activeTodos = computed(() =>
todos.items.filter(todo => !todo.completed)
);
const visibleTodos = computed(() => {
switch (todos.filter) {
case 'active': return activeTodos();
case 'completed': return todos.items.filter(todo => todo.completed);
default: return todos.items;
}
});
effect(() => {
console.log(`You have ${activeTodos().length} active todos`);
});
// Add a todo
todos.items.push({ text: 'Learn Vibrancy', completed: false });import { signal, computed, effect } from 'vibrancy';
const items = signal([
{ price: 10, quantity: 2 },
{ price: 5, quantity: 3 }
]);
const total = computed(() =>
items().reduce((sum, item) => sum + item.price * item.quantity, 0)
);
effect(() => {
console.log(`Total: $${total()}`);
});
// Update items
items.update(list => [...list, { price: 20, quantity: 1 }]);MIT