Breaking Changes
pip install reflexno longer installspydantic; pydantic model support activates when it is installed. Use the newreflex[pydantic]extra (orreflex[db]) to keep it. (#6786)- The compiled frontend now targets React Router 8.3.0 (from 7.18.2), and Reflex requires Node 22.22.0 or newer as a result. Apps on the default generated setup need no
rxconfig.pyor app code changes — Reflex sets nofuture.v8_*flags, and the v8 defaults those flags now enable (middleware, pass-through requests, trailing-slash-aware data requests, the Vite Environment API build path, andsplitRouteModules) are all compatible with the generated app. One change is required if you wrote a custom component againstreact-router-dom: that package no longer exists upstream and is no longer installed, solibrary = "react-router-dom"must becomereact-router(orreact-router/domforRouterProvider/HydratedRouter). (#6854)
Features
- The current
App, the loadedConfig,@rx.pageregistrations, and the bundled-library registry are now scoped to the activeRegistrationContextinstead of module-level globals, so multiple apps (and test harnesses) can coexist in one process without leaking registrations into each other. (#6382) - Report state deltas the frontend cannot process back to the backend via a new
client_errorsocket event, logging an actionable error in the terminal instead of failing silently. A frontend/backend state mismatch is fatal for the session: further events stop until the page is reloaded after the frontend is rebuilt orapi_urlis corrected. (#6827) - Framework logging now flows through standard python
loggingwith per-module loggers (reflex_base.utils.log, re-exported asreflex.utils.log), bootstrapped onimport reflex. Rich colored output is preserved at the sink, andREFLEX_LOG_JSONemits machine-readable JSON-lines records.--loglevel criticalno longer prints the system-info banner (brokenLogLevelstring-compare ordering fixed). (#6863) - The reflex CLI and runtime now log through standard python
loggingper-module loggers, and a new--jsonCLI flag (equivalent toREFLEX_LOG_JSON) emits machine-readable JSON-lines records for agent consumption. (#6865) reflex deployaccepts--min-instancesand--max-instancesto set the autoscaling bounds of an app deployed to Google Cloud. Omitted bounds are left unchanged. (#6884)reflex deploygains--gcp-connection, to pick which of your organization's connected GCP accounts an app deploys through;--full-deploy, to serve the frontend from the provider's own container instead of Reflex's CDN; and--strategy, which was previously only settable in the config file. (#6908)- 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)
Bug Fixes
- Stale
on_loadwork no longer blocks or outlives a page navigation: a newer navigation for the same client now cancels the previous page's unfinishedon_loadevent chain. (#6593) - A
[[...splat]]catchall route no longer matches paths that merely share its prefix —posts/[[...splat]]matched/postsomethingas well as/postsand its descendants, so the wrong page'son_loadevents could fire. (#6790) - Ensure state manager instances use isolated internal locks instead of sharing one lock across instances. (#6830)
- Qualify
dictannotations onBaseStatethat were shadowed byBaseState.dict, so type checkers resolve them to the builtin. (#6846) reflex runnow pre-enables thedevelopmentexport condition for the dev server viaNODE_OPTIONS/BUN_OPTIONS. react-router 8's CLI otherwise re-executes itself to enable the condition, which bun does not honor on installs without node, tripping the CLI's restart guard and exiting the dev server (restartWithMergedOptions() was called, but the process has already been restarted). (#6857)- Pin
REFLEX_ENV_MODEtodevwhen a devAppHarnessinitializes, mirroringreflex run. Previously anAppHarnessProdin the same process leakedREFLEX_ENV_MODE=prodviaexport(), so later dev harnesses compiled with route prerendering enabled and the dev server served prerendered page HTML; on pages with hydration hazards this failed hydration on every load, and events dispatched during the hydration-recovery window were dropped (the vite 8.2.0test_memofull-suite failures). (#6857) - Cache event handler annotations before runtime state-class patches can shadow builtin names on Python 3.14. (#6890)
rx.scripthead updates now flush synchronously instead of via react-helmet's requestAnimationFrame batching, fixing intermittently missing script tags after hydration (flaky "scripts not loaded" failures). (#6905)- Fixed a race where a finishing background task could silently discard state updates made by a concurrently running event handler before they reached the frontend, leaving the UI stale until the next write. Background handlers that never enter
async with selfstill emit their delta, now computed under the state lock. (#6920) AppHarnessstarts the frontend dev server with thedevelopmentexport condition enabled, fixing "Frontend did not start" on node-less (bun-only) installs where react-router's dev CLI restart guard trips. (#6931)- Adding a page no longer raises a spurious
RouteValueErrorwhen a static segment lines up with another route's dynamic segment (e.g./posts/all/[x]alongside/posts/[id]). React Router resolves such siblings in favor of the static one, so only two differently named dynamic segments at the same position conflict. The check was also order-dependent: it only tripped when the bracket-carrying route was added second. (#6953)
Performance
- Remove the per-update
asyncio.create_taskwrapper inEventNamespace.emit_update, cutting scheduling overhead roughly in half for every outgoing state update while keeping the event-loop tick that flushes interim updates before a sync handler resumes. (#6734) - 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) @rx.memocomponents with props bound to state are now auto-memoized at the call site: the state hooks (and event-handler callbacks) those props need compile into a generated wrapper component instead of the page module. A state change re-renders that wrapper rather than the whole page, and React'smemostops there unless one of the prop values actually changed — so binding a Var at the call site scopes an expensive component to exactly the state it reads, instead of coupling it to the page. (#6949)
Documentation
- Documented the
provider,gcp_connectionandfull_deploycloud config settings, including which settings a Google Cloud target ignores and whyfull_deployis left unset rather than false by default. (#6908)
Miscellaneous
- The generated
package.jsonno longer carries a framework-ownedpostcssoverride. The pinnedpostcssdev dependency already forces a single resolved copy for every transitive requirer, so the override was redundant — and because framework overrides are merged intoreflex.lock/package.jsonand never removed from it, an unnecessary one cannot be retired by a later 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, so it changes nothing today and can be deleted by hand. (#6854) - Upgrade the locked dev tooling:
ruff0.15.12 -> 0.16.2,pyright1.1.408 -> 1.1.411,typer0.25.1 -> 0.27.1. Property docstrings are now noun phrases rather than "Get the ..." / "Return the ..." (ruff's newD421), andtyper.main.get_command()is cast toclick.Commandbecause typer 0.27 vendors its own copy of click. (#6893) - The
reflex deploycommand implementation moved out of thereflexpackage intoreflex-hosting-cli, so cloud code is no longer shipped inside the framework. Flags and behavior are unchanged, andreflex-hosting-cliremains a dependency ofreflex, soreflex deployandreflex cloudstay available out of the box. If the package is not installed, these commands now report which package to install instead of failing with a missing-command error. (#6924)