Breaking Changes
-
deploy_urlnow defaults toNoneinstead ofhttp://localhost:3000and is no longer derived fromfrontend_port. The newConfig.frontend_urlproperty returnsdeploy_urlwhen set andhttp://localhost:<frontend_port>otherwise; the sitemap plugin builds absolute links from it. (#7641) -
A
setorfrozensetstate var is now aSetVarrather than anArrayVar, so it no longer type-checks as a sequence in frontend code: indexing, slicing,map,filter,join,reverse,+,*, the comparisons<,<=,>and>=, andcontainswith afieldon it are deprecated. They still compile for now with a deprecation warning and will be removed in 1.0. Call.to_array()first:State.tags[0] # deprecated State.tags.to_array()[0] ``` ([#7642](https://github.com/reflex-dev/reflex/issues/7642))
-
Reading a plain
@propertyof a Pydantic model or dataclass through a state Var now raisesVarAttributeErrorat compile time. The default serializer does not send such a property, so the Var always rendered empty. Use@pydantic.computed_field, or register anrx.serializerthat includes the value. (#7648) -
@rx.eventnow returns anEventHandlerinstead of the decorated function. A decorator stacked above@rx.eventis deprecated: the method still runs and registers under its own name, with a deprecation warning, until 1.0; apply other decorators below@rx.event.@rx.eventon a method of a class that is not a state keeps working when called through an instance, and on astaticmethodorclassmethodleaves it unchanged; both warn and should be removed, and calling such a method through the class (Helper.compute(obj, 3)) is not supported. A module-level function decorated with@rx.eventwhose first parameter is not a state stays a plain function and warns; drop the decorator from plain helper functions. (#7673)
Features
-
Add
rx.client_statefor client-only state. A named var (rx.client_state("", name="query")) is global: any component can read and write it, and the backend canpushandretrieveit. An unnamed var is owned by the outermost page or@rx.memothat uses it, so each@rx.memoinstance gets its own value and a page's values reset when it unmounts. Setting a var re-renders only the components that read it..settakes a value, aFunctionVar, or a lambda (count.set(lambda v: v + 1)) and replaces.set_value, which is deprecated and will be removed in 1.0.rx._x.client_stateremains available with a deprecation warning. (#6936) -
Add
SetVarforset[T]andfrozenset[T]state vars, computed vars and literals, withcontains,length,union,intersection,difference,symmetric_difference,issubset,issuperset,isdisjointandto_arrayoperations. Elements compare by value, so sets of tuples work. The value still travels to the frontend as a JSON array;rx.foreachaccepts a set var directly.frozensetis now a serializable state var type and is accepted byLiteralVar.create. (#7642) -
On a state schema mismatch, a production page now dispatches a cancelable
reflex:state-mismatchevent onwindowbefore it reloads or shows the "new version available" banner. A listener that callsevent.preventDefault()skips both, so the app decides when to reload;event.detailcarries thefrontendandbackendstate schema hashes. (#7653) -
With OpenTelemetry tracing enabled, JSON log records (
REFLEX_LOG_JSON=true) emitted inside a span now include itstrace_idandspan_id, so logs can be correlated with traces. (#7668) -
Add configurable development-time warnings for event handlers that block the asyncio event loop. (#7672)
-
Event handlers are awaitable:
await State.handler(args)inside another event handler runs it right away on the same locked state and returns its result, so a handler may return a value to the backend code awaiting it (returned events still chain, a returned coroutine still raises, and return values are not sent to the frontend yet).@rx.eventreturns theEventHandleritself, andself.handler()keeps calling the plain method as before.@rx.event def add(self, amount: int) -> int: self.total += amount return self.total @rx.event async def add_twice(self, amount: int): first = await type(self).add(amount) yield rx.toast(f"total is now {await type(self).add(amount)}") ``` ([#7673](https://github.com/reflex-dev/reflex/issues/7673))
-
React Compiler now optimizes generated components that use
idrefs, generated UUIDs, and memoized event handlers such as upload callbacks. Thereact_compileroption now installs the experimentalbabel-plugin-react-compilerrelease. Componentidrefs register through the newuseRegisteredRefhook in$/utils/state. Enablingreact_compilernow logs a warning that the option is experimental. (#7694) -
Serialize SQLAlchemy
DeclarativeBasemodels without a custom@rx.serializer: loaded columns and relationships are sent, and unloaded or expired attributes are left out instead of raisingDetachedInstanceErrororMissingGreenlet. (#7756)
Bug Fixes
- Fix uploads triggered by toast actions or
rx.call_scriptcallbacks failing withReferenceError: filesById is not defined. Upload callbacks use the current selected files for their upload ID, including changes made after a toast opens. (#7409) - Use the browser's current route when building the websocket connection's initial hydration event, so navigation during connection setup cannot run the previous page's
on_loadhandler or redirect. (#7522) StringVar.reversed()now reverses Unicode code points, so a character such as an emoji is no longer split into lone surrogates. (#7548)- Give a component
idwritten as an f-string around a Var the same ref name in every build. (#7564) reflex run --jsonshutdown is bounded from the first SIGINT or SIGTERM: every signal is passed on to the app, the app's process tree is killed if it has not exited 15 seconds later, and a consumer that reads slowly or stops reading can no longer hang the supervisor or receive a cut JSON record. (#7650)- Passing a backend var where a Var is built from a Python value, e.g.
rx.Var.create([State._secret]),rx.toast(State._secret),rx.image(src=State._secret),rx.box(id=State._secret)orrx.upload_files(on_upload_progress=State._secret), now raisesBackendVarFormatErrorwith the same explanation asf"{State._secret}", instead of a genericTypeErrororValueError. Converting a backend var to text withstr(),%or!snow raises it too, instead of putting theFieldtext in the page. Using a state var's field fromState.get_fields()in the UI now says to use the var instead of claiming it has no frontend var. (#7651) - Synced
rx.LocalStoragevars in other tabs now reset to their defaults, like a page load, when localStorage is cleared or their key is removed. (#7652) - Fix a
TypeErrorwhen an event handler is called with a proxied state value (such asself.other(self.items)) after one was called with a proxied value of a different kind, such as a dict. (#7673) - Send the state changes a foreground event handler makes after its last
yieldwhen it is cancelled or raises, so the frontend receives them right away. With Redis, they were otherwise not sent until the same var changed again or the page reloaded. (#7696) - The array variant of the ref formatter dropped the f-string Var marker the same way as the singular one, so array ref names no longer contain a per-run hash and stay identical across builds. (#7707)
- Under mypy,
@rx.eventhandlers are typed asEventCallbackinstead ofEventNamespace, so calling one directly no longer fails with"EventNamespace" not callable, and annotations such asrx.event.EventSpecare accepted as types. (#7709) - Fix repeated upload updates that could restore values replaced by newer updates. (#7721)
- The development-time event loop blocking monitor no longer drops its "blocked the event loop for ... in total" report when it is stopped just as the loop recovers. (#7746)
- Calling an event handler with five to eight positional values now type-checks, binding every argument or the leading ones of a longer handler. (#7752)
FunctionStringVar.createaccepts_var_type=EventChain, for a JS function bound to an event trigger as its whole handler. (#7754)- An
EventHandlerstored outside a state, e.g. in a dataclass field, now reads back as the handler for type checkers instead ofAny; read through a state instance it is the bound function. (#7755) - Type SQLAlchemy
synonym()attributes like the attribute they point to, soState.model.aliascompiles and string operations such as.upper()work on it. (#7756) - An awaited event handler now passes through app middleware like a queued event, so a handler that a middleware (such as an auth gate) blocks no longer runs when another handler awaits it; the await returns
None. (#7782) - Fix hooks of a component inside
rx.foreach, such ason_mount,on_unmount, and hooks fromadd_hooks. A hook that used the loop variable crashed the page, and other hooks ran once for the whole list. Each item now runs its own hooks. A loop inside an@rx.memofunction is not covered yet. (#7793)
Performance
- Avoid copying entire mappings when sampling their key and value types during compilation. (#7057)
- Lazy bundled libraries now load only the modules required by each dynamic component instead of loading every registered optional library at once. (#7276)
Miscellaneous
- Add
AdminDashRemovedError, raised byreflexwhen an app uses the removed starlette-admin integration. (#7739)