Example: Shared State (Multi-User Live Counter)¶
app.shared_state holds ReactiveVar instances visible to all connected sessions.
When one user increments the counter, every other open browser tab updates instantly.
See examples/shared_state_demo.py in the repo for the full runnable file.
shared_state_demo.py
import fluxui as ui
app = ui.App("Shared State Demo")
# ── Module-level shared state ─────────────────────────────────────────────────
# This ReactiveVar is created once when the module loads and shared by
# every session. Changes made by any user are visible to all users.
app.shared_state["counter"] = ui.state(0)
app.shared_state["history"] = ui.list_state([]) # log of recent changes
# ── Page ─────────────────────────────────────────────────────────────────────
@app.page("/")
def home(session):
# References to the shared ReactiveVars
counter = app.shared_state["counter"]
history = app.shared_state["history"]
# Per-session state (each user has their own name)
user_name = ui.state("Anonymous")
def increment():
counter.update(lambda v: v + 1)
name = user_name.peek() or "Anonymous"
history.value.insert(0, f"{name} added +1 → {counter.peek()}")
if len(history.value) > 10:
del history.value[10:]
def decrement():
counter.update(lambda v: v - 1)
name = user_name.peek() or "Anonymous"
history.value.insert(0, f"{name} added −1 → {counter.peek()}")
if len(history.value) > 10:
del history.value[10:]
def reset():
counter.set(0)
history.value.insert(0, f"{user_name.peek() or 'Anonymous'} reset the counter")
if len(history.value) > 10:
del history.value[10:]
with ui.Column(gap="var(--s-6)"):
ui.Heading("Live Shared Counter", level=1)
ui.Alert(
"Open this page in multiple browser tabs — they all see the same counter. "
"Every button click is immediately reflected everywhere.",
variant="info",
)
# ── User name ────────────────────────────────────────────────────
with ui.Card(title="Your name"):
ui.TextInput(
label="",
placeholder="Enter your name…",
value=user_name,
on_change=user_name.set,
help_text="Your name will appear in the change history below.",
)
# ── Shared counter ───────────────────────────────────────────────
with ui.Card(title="Shared counter"):
with ui.Row(justify="center", align="center", gap="var(--s-4)"):
ui.Button("−", variant="secondary", on_click=decrement)
ui.Metric(
label="Current value (shared across all sessions)",
value=lambda: str(counter.value), # (1) subscribes to shared ReactiveVar
)
ui.Button("+", on_click=increment)
ui.Button("Reset to 0", variant="ghost", on_click=reset)
# ── Change history ───────────────────────────────────────────────
with ui.Card(title="Recent changes (last 10)"):
if history.value:
for entry in history.value:
with ui.Row(gap="var(--s-2)", align="center"):
ui.Badge("•", variant="info")
ui.Text(entry, variant="caption")
else:
ui.Empty(
title="No changes yet",
description="Click + or − to make the first change.",
icon="📋",
)
if __name__ == "__main__":
app.run(port=8000)
lambda: str(counter.value)is evaluated during theMetric's render, so each session's metric subscribes to the sharedcounter. When any session mutatescounter, all subscribed metrics across all sessions receive_mark_dirty()and are re-rendered.
How Shared State Works¶
Session A (tab 1) Session B (tab 2)
│ │
│ counter.update(v+1) │
│ │
│ counter._notify() ──────────┤
│ │
│ Metric A._mark_dirty() │ Metric B._mark_dirty()
│ re-render → WS patch │ re-render → WS patch
│ │
▼ ▼
Browser tab 1 updates Browser tab 2 updates
The shared ReactiveVar has subscribers from every active session.
When it changes, FluxUI notifies all of them and sends patches over each
session's individual WebSocket.
Thread safety
app.shared_state is designed for simple shared values. For high-concurrency
scenarios, protect mutations with async with app._shared_lock: to avoid
race conditions:
Run it¶
Open http://127.0.0.1:8000 in two browser tabs and click the buttons.