github reflex-dev/reflex v0.9.0

5 hours ago

Release Notes

BREAKING CHANGES

Database support is now opt-in (#6293)

pydantic, sqlmodel, and alembic have been removed from Reflex's base dependencies. If your app uses rx.Model, rx.session, database migrations, or any other DB feature, you must now:

  1. Install the DB extra: pip install 'reflex[db]~=0.9.0'
  2. Set db_url in your rxconfig.py, for example:
config = rx.Config(
    app_name="my_app",
    db_url="sqlite:///reflex.db",
)

Apps that do not use the database can drop these dependencies entirely.

Event processing moved to the backend (#6267)

The primary responsibility for chaining and queueing events has moved from the frontend to the backend. This removes an extra client round-trip when chaining events with yield, but it requires changes for anyone reaching into the internals.

Removed / renamed (Python):

  • App.process_background and rx.app.process are removed — use the new app.event_processor (a BaseStateEventProcessor / EventProcessor) which owns the full event lifecycle, including background tasks.
  • App._background_tasks replaced by App.event_processor._tasks.
  • State.class_substates is removed — use State.get_substates() instead.
  • AppHarness state helpers removed (reflex/testing.py): state_manager, get_state(token), set_state(token, **kwargs), modify_state(token), poll_for_clients(), and _reset_backend_state_manager() are all gone. Tests should drive state changes through the app's event processor / in-app assertions instead.
  • Event.token field removed — the token now lives on EventContext.
  • Event.substate_token replaced by the Event.state_cls property (resolved via the new registry).
  • Delta type refined from dict[str, Any] to dict[str, dict[str, Any]] (nested by substate name).
  • StateManager.create() no longer accepts a state= argument — state classes are discovered from the registry.
  • StateToken (reflex/istate/manager/token.py) replaces raw "client_token_substate" strings across all state managers (disk / memory / redis). StateToken and BaseStateToken are exported from reflex.
  • EventHandlerSetVar.state_cls renamed to .state (to match EventHandler).
  • fix_events removed from the semi-public event API — use Event.from_event_type().
  • get_hydrate_event removed — hydration is now handled internally; simulated pre-hydrated states are gone.

Frontend (state.js):

  • applyEvent() now returns void (previously returned bool); the internal event_processing flag was removed.
  • StateUpdate only includes non-empty fields to reduce bytes over the wire.
  • Params are passed around as a ref instead of inline.

Production mode runs on a single port (#6297)

Production deployments now always serve the frontend and backend on the same port (what used to be --single-port). Consequences:

  • You cannot pass different --frontend-port and --backend-port values in --env PROD.
  • The frontend is now served via Starlette StaticFiles through granian/uvicorn instead of a separate sirv process.
  • The sirv-cli npm dependency has been removed.
  • prerequisites.check_running_mode() now returns a RunningMode enum (FRONTEND_ONLY, BACKEND_ONLY, FULLSTACK) instead of a (bool, bool) tuple.

state_auto_setters default is now False (#6292)

Auto-generated setters (set_<var>) are no longer created by default. Define explicit event handlers for setting state vars, e.g.:

class MyState(rx.State):
    count: int = 0

    @rx.event
    def set_count(self, value: int):
        self.count = value

Existing apps can opt back in temporarily with rx.Config(state_auto_setters=True, ...), but the option is now deprecated and will be removed in 1.0. Explicitly setting state_auto_setters (either value) will emit a deprecation warning.

Other removed deprecations (#6292)

  • PydanticV1 support got deleted (along rx.Base). Please migrate to either dataclasses or PydanticV2.
  • App.overlay_component removed — use extra_app_wraps to inject an overlay component.
  • rx.Model default primary key override: defining your own primary key no longer silently drops the built-in id field. If you need custom PKs on a registered SQLModel class, register it via @rx.ModelRegistry.register and declare the table explicitly.
  • MutableProxy keys in State.get_value(...) are no longer accepted — pass a str key.
  • codeblock in rx.markdown component_map removed — use pre instead to customize code-block rendering.
  • 'string-name' entries in disable_plugins are still supported in 0.9.0 but now targeted for removal in 1.0 (pass Plugin classes directly, e.g. disable_plugins=[SitemapPlugin]).
  • RouterData.page deprecation has been pushed out to 1.0 — still works in 0.9.0, use RouterData.url going forward.

What's Changed

Performance Improvements

Bugfixes

  • Fix race conditions in StateManagerRedis lock detection by @masenf in #6196
  • reflex.event: revert future annotation changes by @masenf in #6254
  • Fix buffered upload handler not cancelling on client disconnect by @FarhanAliRaza in #6307
  • fix: set_focus/blur_focus args stripped by get_handler_args for stateless handlers by @FarhanAliRaza in #6314
  • Serve _upload files with content-disposition: attachment by @masenf in #6303
  • fix: collect imports from components in props in _get_all_imports() by @lawrence3699 in #6317
  • Remove last pydantic.v1 vestiage by @masenf in #6321
  • Fix outstanding frontend_path issues by @masenf in #6328
  • ENG-9348: Lifespan tasks execute in registration order by @masenf in #6334
  • Expose .importable_path on the value returned from rx.asset by @masenf in #6348

Docs

Chores

New Contributors

Full Changelog: v0.8.28...v0.9.0

Don't miss a new reflex release

NewReleases is sending notifications on new releases.