Breaking Changes
get_config(reload=True)has been replaced byreload_config(), and the module-levelbundled_librarieslist inreflex_base.components.dynamichas moved onto the activeRegistrationContext(usebundle_library()/reset_bundled_libraries()as before). (#6382)pydanticis no longer a hard dependency; pydantic model support activates when it is installed. Use thereflex-base[pydantic]extra (orreflex[db]) to keep it. (#6786)- Upgraded the frontend to React Router 8.3.0 (from 7.18.2). Its new baseline requires Node 22.22.0+, so
Node.MIN_VERSIONmoves from 22.12.0 to 22.22.0; the already-pinned React 19.2.8 and Vite 8.0.16 satisfy the React 19.2.7+ and Vite 7+ floors. React Router 8 dropped thereact-router-domre-export package, so it is no longer installed: components that declarelibrary = "react-router-dom"must import fromreact-routerinstead (RouterProvider/HydratedRoutercome fromreact-router/dom). Existing projects have the stale entry pruned frompackage.jsonon the next install. (#6854)
Deprecations
- The
console.debug/info/success/log/warn/error/timinghelpers are deprecated (removal in 1.0) but keep working as shims; uselogging.getLogger(__name__)and the pipeline inreflex_base.utils.loginstead. The interactive Rich features (print/rule/status/ask/progress) remain first-class. (#6867)
Features
RegistrationContextnow carries the loadedConfig, the registeredApp, decorated pages, and bundled libraries, and providesfork()to derive a fresh context that preserves existing registrations while resetting the app and config. (#6382)- Validate incoming state deltas in the frontend before dispatching and report unprocessable updates to the backend via a new
client_errorsocket event instead of failing silently in the browser console. Values reported by a client are escaped and bounded before reaching the backend logs. (#6827) - Added
reflex_base.utils.log: a standard python logging pipeline with a rich-rendering console handler (legacy colors preserved), a JSON-lines handler behindREFLEX_LOG_JSON, record deduplication, and file logging.LogLevelgained a correct total ordering andto_logging_level(), and the interactive console helpers (print/rule/status/progress) now respect JSON mode. (#6863) - The compiled frontend now names what React DevTools shows. Every memoized component carries a
displayNametaken from the Python component class or@rx.memofunction it was generated from, instead of rendering asAnonymous; every generated context (ColorModeContext,UploadFilesContext,DispatchContext,EventLoopContext,ThemeContext, and one per state) is named, so the provider stack reads asStateContext(reflex___state____state.my_state).Providerrather than an unlabelledContext.Provider; each page is labelled with its route (Component(blog/[slug])) instead of a bareComponent; and client-only (NoSSRComponent) wrappers render asClientSide(<Tag>). (#6945) - Add the
REFLEX_REFERRER_PARAMenvironment variable, read at compile time to append arefquery parameter to the "Built with Reflex" badge link. (#6951)
Bug Fixes
- Event handlers marked with
@rx.event(supersedes=True)now use latest-wins semantics: enqueuing a new invocation cancels the previous unfinished event chain for the same client token.on_load_internaluses this to cancel staleon_loadchains on navigation. (#6593) @rx.memofunctions that forward props throughrx.RestPropnow classify those props the same way a regular component does: a forwarded prop that is not a declared prop of the target (e.g.font_weight=) joins the component'sstyleand renders ascss, instead of being passed through as an unrecognized prop and silently dropped. Such props merge with an explicitstyle=rather than replacing it, and props the target actually declares are still forwarded normally. (#6605)- Declare
rx.plugins.RadixThemesPlugin()in therxconfig.pywritten byreflex init, so freshly scaffolded apps no longer emit the implicit Radix Themes enablement deprecation warning. (#6776) - Qualify annotations whose bare builtin name is shadowed by a member of the same class, so
Var.createis no longer inferred asLiteralBooleanVarfor every argument type.Var.bool,BaseComponent.setandPropsBase.dictshadowedbool,setanddictfor annotations elsewhere in their own class bodies, which type checkers resolve against the class namespace. (#6846) - The
vite previewserver that react-router prerendering fetches pages from is now pinned to127.0.0.1, fixingreflex exportfailing withPrerender: Request failed for /: ECONNREFUSEDin environments wherelocalhostresolves to both IPv4 and IPv6 loopback addresses (such as docker containers). (#6857) - Resolve event handler annotations before runtime state-class patches can shadow builtin names on Python 3.14. (#6890)
- Chained events (those yielded by an event handler) now inherit the routing data of the event being processed, so
routerand dynamic route args resolve against the view that produced them instead of whichever view the last client-sent event left on the root state. (#6919) - Stop background event handlers from computing a delta and cleaning the root state after the state lock is dropped. On a shared state tree (opportunistic locking, in-memory state manager) that unlocked snapshot-then-clean raced concurrent foreground handlers: a write landing between the background task's dirty-var snapshot and its
_clean()was silently discarded and never reached any delta. Background state changes are emitted by theirasync with selfcontext exits, which hold the lock. (#6920) - Resolve
TypeAliasTypeannotations (PEP 695typestatements and thetyping_extensionsbackport) to their underlying value inVar.guess_type, so state vars annotated with an alias liketype Key = Literal["day", "week"]compile instead of raisingTypeError: Unsupported type ... for guess_type. Parameterized generic aliases (Keys[str]fortype Keys[T] = list[T]) and aliases nested in unions (Key | None) are resolved as well. (#6944)
Performance
- Dev mode no longer pays for React's per-element owner-stack capture: navigation clicks in a large app dropped from ~350ms to ~83ms of main-thread CPU (5.6x prod down to ~1.3x). In exchange
React.captureOwnerStack()returns no owner frames in dev, which affects React DevTools' owner-stack view and custom error overlays built on that API; setREFLEX_REACT_OWNER_STACKS=1to restore them. (#6905) MemoComponentinstances no longer opt out of compiler auto-memoization wholesale. Only the passthrough wrappers the auto-memoize pass generates do, tracked by the newauto_memo_wrapperflag onMemoComponentDefinition, so state-bound props and event handlers on a@rx.memocall site compile their hooks into a generated wrapper instead of the enclosing page. (#6949)
Miscellaneous
- Removed the
postcssentry fromPackageJson.OVERRIDES, leaving that mapping empty.postcssis pinned directly inDEV_DEPENDENCIES(8.5.23), and a top-level pin already satisfies and dedupes every transitive requirer (autoprefixer,postcss-import, andvite's own^8.5.15), so the override resolved to the same single copy while being strictly harder to retire: framework overrides are merged into a project'sreflex.lock/package.jsonand never removed from it, so an override cannot be undone by a later Reflex release. Projects that already installed 0.9.8 keep an inert"postcss": "8.5.23"override inreflex.lock/package.json; it matches the dev-dependency pin and can be deleted by hand. (#6854) - Bump bundled
vitefrom 8.0.16 to 8.2.0. The full-integration-suite memo regression that previously held the pin back was root-caused to aREFLEX_ENV_MODE=prodleak betweenAppHarnessinstances (fixed inreflex.testing), not to a vite defect. (#6857) - Internal logging in reflex-base migrated from the legacy console helpers to standard python
loggingper-module loggers. (#6864) - Property docstrings are now noun phrases rather than "Get the ..." / "Return the ..." (ruff 0.16's new
D421).chain_updates()declares itseventsparameter asAny, matching the runtime validation it delegates to.Field.default,Field.default_factory, andField.default_value()now admitNone: a field whose annotated type has no computed default is given aNonedefault and has its recorded annotation widened to match, so the static types were previously understating what these can hold. Internally,ImportVarandunionizeare imported from the modules that define them instead of by way ofreflex_base.vars.base, and two deadis not Noneguards were dropped from_isinstance()and the dependency-tracking bytecode scanner. (#6893)