Build a Live Counter Page with Reflex: A Step-by-Step Guide
Build a reactive counter with Reflex using rx.State event handlers, run it with the dev server, and verify live updates without page reloads.
06 Jun 2026, 14:05 UTC

Problem: Creating Reactive UI Without Manual DOM Updates
You need a single-page Reflex app where clicking buttons updates a number instantly—no page reloads, no JavaScript frameworks beyond Reflex. The challenge is wiring state and event handlers correctly so updates propagate live through the backend.
Prerequisites
- Python 3.9 or newer installed
- pip available
- Internet connection to install Reflex
- Terminal access
Verify Reflex is installed:
pip install reflex
reflex --version
If the second command fails, the installation did not complete successfully.
Creating the Counter State
Define a state class that holds the counter value. Reflex tracks changes made inside state event methods.
Create app.py with the following:
import reflex as rx
class CounterState(rx.State):
count: int = 0
def increment(self):
self.count += 1
def decrement(self):
self.count -= 1
Building the UI Component
Construct the page using Reflex components. Pass event handlers as unbound methods—never call them with parentheses in props.
Add this to the same app.py:
def index() -> rx.Component:
return rx.vstack(
rx.text(CounterState.count),
rx.hstack(
rx.button("Decrement", on_click=CounterState.decrement),
rx.button("Increment", on_click=CounterState.increment),
),
)
app = rx.App()
app.add_page(index)
Running the Development Server
Start the Reflex dev server from your project folder:
reflex run
The CLI prints a URL (typically http://localhost:3000). Open it in a browser.
Expected Checks
- The page loads with a number displayed (starting at 0)
- Clicking Increment increases the number by 1
- Clicking Decrement decreases the number by 1
- No full page reload occurs after clicks
- Terminal and browser console show no errors
Common Recovery Options
If the counter does not update:
- Handler reference: Ensure
on_click=CounterState.increment(no parentheses). UsingCounterState.increment()runs the handler at render time. - State annotation: Confirm
count: int = 0is typed. Untyped state does not trigger reactivity. - Page reading: Verify the page reads
CounterState.count, not a local variable. - Server restart: Stop the CLI (Ctrl+C) and run
reflex runagain after edits. - Port conflict: If the CLI reports the port is in use, run
reflex run --port 3001to use another port.
Limitations and Version Sensitivity
Reflex APIs and component props evolve between releases. The patterns shown work with Reflex 0.6.x and later. Before adopting, run reflex --version and check the official changelog if you encounter unexpected behavior.
Avoid long-running or blocking work in event handlers; it can freeze the UI. For heavy computation, use background tasks or separate threads.