Reaktiv
Signals for Python - inspired by Angular Signals / SolidJS. Reactive Declarative State Management Library for Python - automatic dependency tracking and reactive updates for your application state.
Install / Use
/learn @buiapp/ReaktivREADME
reaktiv
<div align="center">
Reactive Declarative State Management Library for Python - automatic dependency tracking and reactive updates for your application state.
Website | Live Playground | Documentation | Deep Dive Article
</div>Installation
pip install reaktiv
# or with uv
uv pip install reaktiv
reaktiv is a reactive declarative state management library that lets you declare relationships between your data instead of manually managing updates. When data changes, everything that depends on it updates automatically - eliminating a whole class of bugs where you forget to update dependent state.
Think of it like Excel spreadsheets for your Python code: when you change a cell value, all formulas that depend on it automatically recalculate. That's exactly how reaktiv works with your application state.
Key benefits:
- 🐛 Fewer bugs: No more forgotten state updates or inconsistent data
- 📋 Clearer code: State relationships are explicit and centralized
- ⚡ Better performance: Only recalculates what actually changed (fine-grained reactivity)
- 🔄 Automatic updates: Dependencies are tracked and updated automatically
- 🎯 Python-native: Built for Python's patterns with full async support
- 🔒 Type safe: Full type hint support with automatic inference
- 🚀 Lazy evaluation: Computed values are only calculated when needed
- 💾 Smart memoization: Results are cached and only recalculated when dependencies change
Documentation
Full documentation is available at https://reaktiv.bui.app/docs/.
For a comprehensive guide, check out The Missing Manual for Signals: State Management for Python Developers.
Quick Start
from reaktiv import Signal, Computed, Effect
# Your reactive data sources
name = Signal("Alice")
age = Signal(30)
# Reactive derived data - automatically stays in sync
@Computed
def greeting():
return f"Hello, {name()}! You are {age()} years old."
# Reactive side effects - automatically run when data changes
# IMPORTANT: Must assign to variable to prevent garbage collection
greeting_effect = Effect(lambda: print(f"Updated: {greeting()}"))
# Just change your base data - everything reacts automatically
name.set("Bob") # Prints: "Updated: Hello, Bob! You are 30 years old."
age.set(31) # Prints: "Updated: Hello, Bob! You are 31 years old."
Core Concepts
reaktiv provides three simple building blocks for reactive programming - just like Excel has cells and formulas:
- Signal: Holds a reactive value that can change (like an Excel cell with a value)
- Computed: Automatically derives a reactive value from other signals/computed values (like an Excel formula)
- Effect: Runs reactive side effects when signals/computed values change (like Excel charts that update when data changes)
# Signal: wraps a reactive value (like Excel cell A1 = 5)
counter = Signal(0)
# Computed: derives from other reactive values (like Excel cell B1 = A1 * 2)
@Computed
def doubled():
return counter() * 2
# Effect: reactive side effects (like Excel chart that updates when cells change)
def print_values():
print(f"Counter: {counter()}, Doubled: {doubled()}")
counter_effect = Effect(print_values)
counter.set(5) # Reactive update: prints "Counter: 5, Doubled: 10"
Excel Spreadsheet Analogy
If you've ever used Excel, you already understand reactive programming:
| Cell | Value/Formula | reaktiv Equivalent |
|------|---------------|-------------------|
| A1 | 5 | Signal(5) |
| B1 | =A1 * 2 | Computed(lambda: a1() * 2) |
| C1 | =A1 + B1 | Computed(lambda: a1() + b1()) |
When you change A1 in Excel, B1 and C1 automatically recalculate. That's exactly what happens with reaktiv:
# Excel-style reactive programming in Python
a1 = Signal(5) # A1 = 5
@Computed # B1 = A1 * 2
def b1() -> int:
return a1() * 2
@Computed # C1 = A1 + B1
def c1() -> int:
return a1() + b1()
# Display effect (like Excel showing the values)
display_effect = Effect(lambda: print(f"A1={a1()}, B1={b1()}, C1={c1()}"))
a1.set(10) # Change A1 - everything recalculates automatically!
# Prints: A1=10, B1=20, C1=30
Just like in Excel, you don't need to manually update B1 and C1 when A1 changes - the dependency tracking handles it automatically.
graph TD
%% Define node subgraphs for better organization
subgraph "Data Sources"
S1[Signal A]
S2[Signal B]
S3[Signal C]
end
subgraph "Derived Values"
C1[Computed X]
C2[Computed Y]
end
subgraph "Side Effects"
E1[Effect 1]
E2[Effect 2]
end
subgraph "External Systems"
EXT1[UI Update]
EXT2[API Call]
EXT3[Database Write]
end
%% Define relationships between nodes
S1 -->|"get()"| C1
S2 -->|"get()"| C1
S2 -->|"get()"| C2
S3 -->|"get()"| C2
C1 -->|"get()"| E1
C2 -->|"get()"| E1
S3 -->|"get()"| E2
C2 -->|"get()"| E2
E1 --> EXT1
E1 --> EXT2
E2 --> EXT3
%% Change propagation path
S1 -.-> |"1\. set()"| C1
C1 -.->|"2\. recompute"| E1
E1 -.->|"3\. execute"| EXT1
%% Style nodes by type
classDef signal fill:#4CAF50,color:white,stroke:#388E3C,stroke-width:1px
classDef computed fill:#2196F3,color:white,stroke:#1976D2,stroke-width:1px
classDef effect fill:#FF9800,color:white,stroke:#F57C00,stroke-width:1px
%% Apply styles to nodes
class S1,S2,S3 signal
class C1,C2 computed
class E1,E2 effect
%% Legend node
LEGEND[" Legend:
• Signal: Stores a value, notifies dependents
• Computed: Derives value from dependencies
• Effect: Runs side effects when dependencies change
• → Data flow / Dependency (read)
• ⟿ Change propagation (update)
"]
classDef legend fill:none,stroke:none,text-align:left
class LEGEND legend
Additional Features That reaktiv Provides
Lazy Evaluation - Computations only happen when results are actually needed:
# This expensive computation isn't calculated until you access it
@Computed
def expensive_calc():
return sum(range(1000000)) # Not calculated yet!
print(expensive_calc()) # NOW it calculates when you need the result
print(expensive_calc()) # Instant! (cached result)
Memoization - Results are cached until dependencies change:
# Results are automatically cached for efficiency
a1 = Signal(5)
@Computed
def b1():
return a1() * 2 # Define the computation
result1 = b1() # Calculates: 5 * 2 = 10
result2 = b1() # Cached! No recalculation needed
a1.set(6) # Dependency changed - cache invalidated
result3 = b1() # Recalculates: 6 * 2 = 12
Fine-Grained Reactivity - Only affected computations recalculate:
# Independent data sources don't affect each other
a1 = Signal(5) # Independent signal
d2 = Signal(100) # Another independent signal
@Computed # Depends only on a1
def b1():
return a1() * 2
@Computed # Depends on a1 and b1
def c1():
return a1() + b1()
@Computed # Depends only on d2
def e2():
return d2() / 10
a1.set(10) # Only b1 and c1 recalculate, e2 stays cached
d2.set(200) # Only e2 recalculates, b1 and c1 stay cached
This intelligent updating means your application only recalculates what actually needs to be updated, making it highly efficient.
The Problem This Solves
Consider a simple order calculation:
Without reaktiv (Manual Updates)
class Order:
def __init__(self):
self.price = 100.0
self.quantity = 2
self.tax_rate = 0.1
self._update_totals() # Must remember to call this
def set_price(self, price):
self.price = price
self._update_totals() # Must remember to call this
def set_quantity(self, quantity):
self.quantity = quantity
self._update_totals() # Must remember to call this
def _update_totals(self):
# Must update in the correct order
self.subtotal = self.price * self.quantity
self.tax = self.subtotal * self.tax_rate
self.total = self.subtotal + self.tax
# Oops, forgot to update the display!
With reaktiv (Excel-style Automatic Updates)
This is like Excel - change a cell and everything recalculates automatically:
from reaktiv import Signal, Computed, Effect
# Base values (like Excel input cells)
price = Signal(100.0) # A1
quantity = Signal(2) # A2
tax_rate = Signal(0.1) # A3
# Formulas (like Excel computed cells)
@Computed # B1 = A1 * A2
def subtotal():
return price() * quantity()
@Computed # B2 = B1 * A3
def tax():
return subtotal() * tax_rate()
@Computed # B3 = B1 + B2
def total():
return subtotal() + tax()
# Auto-display (like Excel chart that updates automatically)
total_effect = Effect(lambda: print(f"Order total: ${total():.2f}"))
# Just change t
Related Skills
node-connect
353.1kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
claude-opus-4-5-migration
111.6kMigrate prompts and code from Claude Sonnet 4.0, Sonnet 4.5, or Opus 4.1 to Opus 4.5
frontend-design
111.6kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
model-usage
353.1kUse CodexBar CLI local cost usage to summarize per-model usage for Codex or Claude, including the current (most recent) model or a full model breakdown. Trigger when asked for model-level usage/cost data from codexbar, or when you need a scriptable per-model summary from codexbar cost JSON.
