Reflex State Lifecycle: Using on_mount and on_unmount for Clean Resource Management
Reflex State supports on_mount and on_unmount lifecycle hooks for starting and cleaning up server-side work. Use them to avoid leaked timers and connections when users navigate away.
04 Sept 2026, 11:40 UTC

You build a Reflex page that starts a background poller, opens a database connection, or subscribes to a websocket when the user arrives. If the user navigates away, the Python State object that drove the page should stop that work. Without a clear teardown point, timers keep firing, connections linger, and the server accumulates stray state.
The useful takeaway is that Reflex State supports component-like lifecycle hooks, on_mount and on_unmount, which let you treat Python state as a managed resource. You can start side effects when the state is attached to a browser session and clean them up deterministically when it detaches.
The problem with long-lived Python state in a reactive UI
Reflex maps Python functions to React components and keeps a server-side State class in sync with the browser via JSON over a websocket. State is the single source of truth for a page. When a browser connects, Reflex instantiates the State for that session. When the session ends or the user navigates, the instance should be torn down.
Because the UI is declarative, it is easy to start work in an event handler and forget to stop it. The lifecycle hooks give an explicit place for setup and cleanup without writing JavaScript.
How on_mount and on_unmount work
State is a Reflex base class. You can define async methods named on_mount and on_unmount. Reflex calls on_mount once when the state is first attached to a client session and calls on_unmount when the session ends.
This is not a React effect. It runs on the Python server, inside the state instance for that user. It is a good place to start polling, acquire a connection, or register a subscription, and to cancel it on unmount.
Worked example: a stateful timer with explicit cleanup
Create a project and edit the main state file. Run commands in a terminal at the project root with a Python environment where reflex is installed.
reflex init my_app
cd my_app
reflex runExpected check: the dev server starts and opens a browser at localhost:3000. Risk: the default port may be in use; Reflex will report a bind error. Hot-reload is provided by a built-in file watcher, which may be limited on Windows due to file system event differences.
Example state with lifecycle hooks:
import reflex as rx
class TimerState(rx.State):
seconds: int = 0
_running: bool = False
async def on_mount(self):
# start work when the state attaches to a browser session
self._running = True
# in a real app you might start a background task here
async def on_unmount(self):
# clean up when the session ends
self._running = False
# cancel tasks, close connections here
def tick(self):
if self._running:
self.seconds += 1
def reset(self):
self.seconds = 0The State class is serialized to JSON and sent to the client. Only JSON-serializable attributes like int, str, list, dict are reliably synced. Complex Python objects require custom handlers.
Trade-off: JSON serialization is the contract
Automatic sync is convenient because you do not write manual websocket messages. The cost is that state must be JSON-serializable. Non-serializable objects, open file handles, or custom classes will break the sync between Python and the React frontend.
Practical check: run the app and open browser developer tools on the Network tab. Inspect websocket frames to see JSON payloads exchanged between client and server. Modify a Python source file and observe automatic browser refresh without manual restart.
API stability is another limitation. Reflex is evolving and breaking changes can occur between minor versions. Pin the dependency in production, review release notes before upgrading, and confirm the exact on_mount and on_unmount trigger behavior against the documentation for the version you install.
Actionable closing
Audit each State class for side effects. If you start work, add a matching cleanup in on_unmount. Keep state attributes JSON-friendly and move non-serializable resources out of State into short-lived helpers that are created and disposed in the lifecycle hooks.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.