Breaking Changes
Python and dependencies
- Drop support for Python 3.10, which reaches end of life in October 2026. Reflex now requires Python 3.11 or newer. (#7449)
reflex[db]allows SQLModel 0.0.45 and later, which stores a plaindatetimefield as UTC: writes and filters need timezone-aware values and reads return aware ones. To keep naive datetimes, declare the field withsa_type=DateTime(timezone=False)or pinsqlmodel<0.0.45, as described in Datetimes and SQLModel upgrades. (#7462)
State
- Reading a backend var on a state class (
State._items) now returns itsFielddescriptor instead of the var's default value. Passing it to a component, which used to bake the default into the frontend, now raisesChildrenTypeErroras a child orTypeError: Unsupported type <class 'reflex_base.vars.base.Field'> for LiteralVar.as a prop. To keep baking in the default, passState._items.default_value()instead; to make the value reactive, declare a frontend var or computed var and pass that. See Upgrading to Reflex 0.10. (#7312) - Assigning a state var through its state class, such as
State.count = 10, raisesTypeErrorinstead of replacing the var; so do pytest'smonkeypatch.setattrandunittest.mock.patch.objecton a var. Change a default withState.__fields__["count"].set_default(10), patch the field in tests, and declare class-level configuration asClassVar. See Upgrading to Reflex 0.10. (#7516) - Mutable values provided as state var defaults are deep-copied when assigned at class creation time or passed to
set_default, so later changes to the original object no longer reach new sessions. A module-level list filled in after theclassstatement, such asoptions: list[str] = OPTIONSfollowed byOPTIONS.append(...), now starts every session empty: fill it before the class is defined, or declaredefault_factory=lambda: list(OPTIONS). (#7519) - In a background task, calling a state-modifying handler inherited from a parent state, or writing an inherited var, outside
async with selfnow raisesImmutableStateErrorlike any other state change. See Upgrading to Reflex 0.10. (#7312) - Assigning an undeclared state attribute still raises
SetUndefinedStateVarErroroutside of prod mode, but no longer in prod. The internal class mapsbackend_vars,inherited_varsandinherited_backend_vars,get_skip_vars()and the instance_backend_varsare removed: useget_fields(), whose fields know the state they belong to. (#7312) - Instances of Reflex 0.9 and 0.10 can no longer share a Redis or disk state store. A 0.10 instance loads state saved by 0.9, but a 0.9 instance discards state saved by 0.10, so a rolling deploy that runs both versions, or a rollback to 0.9 against the same store, resets the sessions that reach the older instance. Upgrade every instance of an app together, and clear the store (or start a fresh one) when rolling back. (#7494)
CLI
- Remove the
reflex componentCLI (init,build,share,install) and theCustomComponentsconstants; runningreflex componentnow points to the replacements. Wrap React libraries directly in your app as described in the wrapping React docs, and start reusable component packages from the component template, which builds, tests, and publishes them with standard Python tooling. (#6425, #7497)
Deprecations
- Deprecate
State.router.headers.cookieandState.router.headers["cookie"]in components; both now render an empty string. Userx.Cookiefor cookies that need to be accessible to the frontend. (#7360)
Features
- A substate may now declare a var or computed var with the same name as an inherited var: it gets an independent one of its own, instead of raising
BaseVarShadowsInheritedVarErrororComputedVarShadowsBaseVarsError. A dynamic route arg only conflicts with a var of the state it is installed on. (#7312) - The duration settings read by the app and the state managers take a unit suffix:
SQLALCHEMY_POOL_TIMEOUT,REFLEX_SOCKET_INTERVALandREFLEX_SOCKET_TIMEOUTaccept values such as2m, andREFLEX_AUTO_RELOAD_COOLDOWN,REFLEX_OPLOCK_HOLD_TIMEandREFLEX_STATE_MANAGER_DISK_DEBOUNCEreplace the_MS/_SECONDSnames, which still work with a deprecation warning until 1.0. A bare number is read as seconds. (#7138) - Set
REFLEX_REDIS_MAX_CONNECTIONSto cap each Redis client's connection pool (the state manager, the token manager and the health check each use their own client). Once a pool reaches the cap, requests wait up toREFLEX_REDIS_POOL_TIMEOUT(default 2s, which must be above 0 and below the configured state-lock lifetime) for a free connection instead of opening new ones. (#7179)
Bug Fixes
State and events
- Stop sending request cookies, including HttpOnly cookies, and standard authorization, Cloudflare Access, OAuth2 Proxy, AWS ALB, and Google IAP credential headers to frontend router data. On-load events no longer copy request router metadata to the frontend; server-side access to request headers is unchanged. (#7360)
- Defaults are no longer part of the saved-state schema, so changing one keeps state saved by this release or later loadable. A browser storage var annotated with a storage type, such as
rx.Field[rx.LocalStorage], and declared with adefault_factorycompiles with the storage options the factory produces, andreset()restores the factory's value. (#7461) - Assigning a double-underscore private attribute on a state (
self.__counter = 1) from a mixin, a base, or a class whose name starts with an underscore no longer raisesSetUndefinedStateVarErrorin dev mode. Private names not explicitly declared withrx.field()are plain Python attributes: vars do not update in response to them. (#7465) - In a background task on a substate, in-place changes to a mutable var inherited from a parent state (like
self.items.append(...)) are now sent to the client and persisted. (#7312) - A page URL with a
selfquery parameter (e.g./post?self=1), or a request header namedself, no longer crashes router data parsing and leaves the page unhydrated. (#7324) - A state stored in Redis that can no longer be unpickled, for example because a deploy moved or deleted a class held in a state var, is now replaced with a fresh state like a schema mismatch, instead of failing every event from that tab until the Redis key expires. (#7329)
- The memory and disk state managers now free expired session states right away instead of waiting for a garbage collection pass, and the disk state manager no longer keeps a lock for every expired session. (#7318)
- Fix a race with
REFLEX_OPLOCK_ENABLEDwhere an instance could take an opportunistic lease before its Redis lock notifications were active, making other instances wait out the full hold time for the same token. (#7372) - Fix
TypeError: refs._client_state_set... is not a functionwhen a component sets a globalrx._x.client_statevalue before any component reading.valuehas mounted, such as when the reader sits behind anrx.cond. (#7286) - Preserve ID-based form controls through automatic memoization while excluding IDs on non-controls from submissions. (#7227)
Database
- The
dbextra now installsgreenlet, which SQLAlchemy 2.1 no longer pulls in on its own, sorx.Model,rx.session()and thereflex dbcommands work on a freshpip install reflex[db]instead of failing withImportError: The SQLAlchemy asyncio module requires that the Python 'greenlet' library is installed. (#7466) - Fixed
reflex db makemigrations/migratecrashing withCompileErrorwhen autogenerating a migration that adds a column with a callable default (e.g.default_factory=datetime.nowordefault=uuid.uuid4) to an existing table; callable defaults are now evaluated before being carried as a SQLserver_default. (#6706) reflex dbcommands only requiresqlalchemyandalembic, so apps that use plain SQLAlchemy models withoutsqlmodelcan run migrations again. (#7322)reflex dbcommands run without thedbextra installed now exit with the "pip install reflex[db]" message instead of a raw traceback. (#7259)
CLI, build and serving
- Serve valid dynamic-route URLs (e.g.
/articles/7) with HTTP 200 instead of 404 when loaded directly in self-hosted prod static serving, reserving 404 for genuinely unknown paths. (#6996) reflex runnow stops its frontend on SIGTERM and SIGINT without a TTY, while keeping frontend workers in the CLI process group so a hard kill also stops them. (#7328)reflex run --jsonnow emits every output line as a JSON record:print()output from the app, subprocess output, and worker tracebacks (as one record with anexceptionfield) no longer break the JSON-lines stream. (#7350)- Fix stylesheet edits in
assets/not applying in dev mode until a manual reload. (#7317) - Avoid reinstalling frontend packages on every compile or hot reload when bun or npm only changes the formatting of
package.json. (#7236) - When a project keeps using npm because
reflex.lock/only haspackage-lock.json(for example after a run withREFLEX_USE_NPM=1), Reflex now logs why and how to switch back to bun withREFLEX_USE_NPM=0. (#7093) - Reflex now checks the Node.js version before running npm, so an unsupported Node.js no longer leaves npm lockfiles behind that switch later runs to npm. (#7210)
- An editable install (
uv sync,pip install -e .) no longer overwrites.pyistubs that the checkout already has. A checkout missing any of them still gets them generated. (#7303) - On Windows, the development backend no longer closes its listening socket twice when it releases the port, which could close another socket that had reused the handle and make it fail with
OSError: [WinError 10038]. (#7348) - Stop logging
asyncio.CancelledError: lifespan_cleanupas an error when a running coroutine lifespan task is cancelled at backend shutdown or hot reload. (#7392)
Testing
AppHarnessnow forgets the states of every module of the app's package when it stops, not only those of the app module, so a state defined elsewhere in the app (for example one usingrx.dynamic) no longer breaks the next app started in the same test process. (#7359)
Performance
- Speed up first page loads by combining hydration with the websocket connect and sending only values that differ from compiled defaults. Reduce Redis state-tree read/write overhead and avoid repeated class metadata computation in apps with many states. (#7064)
- Reading a state var is about 4x faster, and setting one about 7x faster (15x in prod mode): vars, computed vars and event handlers are now descriptors on the state class that declares them, instead of every attribute access going through
BaseState.__getattribute__. (#7312) - Process events with less CPU on the backend: iterating, sorting and reading list and dataclass state values costs 25–75% less, and the redis state manager writes the changed states of a session in one round trip. (#7370)
- Apps that define an
rx.SharedStateno longer pay extra per-event work for events that change no shared state. (#7237) - Compile memoized components faster by reusing unchanged memo bodies instead of rendering them again. (#7123)
Documentation
- Add an Upgrading to Reflex 0.10 guide covering the changes most likely to break an existing app, with the fix for each. (#7496)
- Document the dict form for
rx.toastactionandcancelprops instead of referencing the non-publicToastActionclass. (#7327)
Miscellaneous
- Allow wrapt 2.4 and 2.5. (#7424)