reflex-base Changelog
v0.9.10 (2026-09-01)
Bug Fixes
- Enqueuing an event chained from a parent event that has already finished no longer raises
RuntimeError: Cannot add a child to an EventFuture that is already done.. Such a late-chained event skips registration under the completed parent and is processed normally. (#6801) - Compiling unchanged source twice now produces the same files. Import order came from a
set, whose iteration order varies withPYTHONHASHSEED, and a library's imports were not deduplicated against themselves; both fed the content hash a memoized component is named from, so each compile renamed those components and invalidated downstream build caches for no reason. (#7012)
Performance
- Compiling an app with many components is substantially faster. Imports are now deduplicated as they are merged rather than only at the end, so naming a memoized component no longer costs work proportional to the size of its subtree. An app with 80 routes and 42,268 components went from 263s to 48s. (#7012)
v0.9.9 (2026-08-28)
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). Readingreflex_base.components.dynamic.bundled_libraries(orDEFAULT_BUNDLED_LIBRARIES) still works as a deprecated shim that resolves against the active context; the shims are removed in 1.0. (#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) get_config(reload=True)is deprecated (removal in 1.0) but keeps working: passingreload=Trueemits a deprecation warning and delegates toreload_config(), which forces a fresh load of the config into the currentRegistrationContext. (#6985)
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) - Compiled components are now named for React DevTools: memoized components take a
displayNamefrom the Python class or@rx.memofunction they came from instead of showing asAnonymous, generated contexts are named (StateContext(reflex___state____state.my_state).Providerrather than an unlabelledContext.Provider), pages are labelled with their route (Component(blog/[slug])), 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) a concurrent foreground write landing between the background task's dirty-var snapshot and its
_clean()was silently discarded and never reached any delta. (#6920) - Resolve
TypeAliasTypeannotations (PEP 695typestatements and thetyping_extensionsbackport) to their underlying value inVar.guess_type,_isinstanceandtypehint_issubclass. A state var annotated with an alias liketype Key = Literal["day", "week"]now compiles and can be assigned to, and event handlers with alias-annotated arguments can be passed uncalled to event triggers. Parameterized generic aliases (Keys[str]fortype Keys[T] = list[T]) and aliases nested in unions (Key | None) are resolved as well. (#6944, #6986) - The generated
vite.config.jsno longer passes options rejected or deprecated by rolldown-vite 8.x: the no-oprollupOptions.jsxkey is dropped andoutput.advancedChunksmigrated tooutput.codeSplitting(same shape), removing the "Invalid input options" and "advancedChunks option is deprecated" warnings from every prod build/export. (#6987) - Console warnings and errors no longer print literal backslash-escaped brackets (e.g.
dict\[str, str]). The rich-markup escapes were left over from the legacy console helpers, but the logging pipeline renders messages with markup disabled, so bracketed type names now print verbatim.VarAttributeErrormessages drop the same escapes. (#6989) - A custom component declaring
library = "react-router-dom"(including versioned and subpath forms) now fails compilation with an actionable error naming the component, instead of silently installing the removed package — which pulled in a second, unpinned React Router 7 copy that worked in dev by accident and broke production builds. React Router 8 droppedreact-router-dom: uselibrary = "react-router"instead, or"react-router/dom"forRouterProvider/HydratedRouter. (#6991) - With
REFLEX_ENABLE_FULL_LOGGING, granian worker records reach the log file again: the file handler now opens in append mode (truncating once up front), so it reopens after the worker's post-forklogging.config.dictConfigcloses it instead of silently dropping every worker-side record. The legacy console file writer follows the reopened stream through a proxy, so its timestamped lines no longer leak to stdout and break--jsonoutput. (#6992)
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)- Speed up prop validation and JSON serialization on hot paths:
_isinstancenow reads__origin__once per type, memoizesget_args, and caches the deferredVar/LiteralVar/Fieldimports instead of re-importing on every call, andjson_dumpscaches its deferredserializers.serializelookup. (#6862) vite_config_templatedeclares aresolveIdhook filter ({ id: /react-dom\/server/ }) onvite-plugin-always-use-react-dom-server-node. The plugin runs withenforce: "pre", so without a filter rolldown invoked its JS handler for every import specifier in the graph (~15,800 calls on the Reflex docs build) to redirect the one specifier imported byentry.server.node.tsx. The template also imports./vite-plugin-safari-cachebust.jswith its extension, which Vite'sconfigLoader: "native"requires. (#6959)
Miscellaneous
- Removed the
postcssentry fromPackageJson.OVERRIDES, leaving that mapping empty.postcssis pinned directly inDEV_DEPENDENCIES(8.5.23), and that top-level pin already dedupes every transitive requirer (autoprefixer,postcss-import, andvite's own^8.5.15). 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. (#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).Field.default,Field.default_factoryandField.default_value()now admitNone, matching what they already hold for a field whose annotated type has no computed default, andchain_updates()declares itseventsparameter asAny, matching the runtime validation it delegates to. (#6893)
v0.9.8 (2026-08-04)
Features
- Add
Env.PREVIEW, theVITE_MINIFYandREFLEX_NO_AUTOPREFIXERenvironment variables, aminify/cssMinifyoption on the vite config template, and aREFLEX_NO_AUTOPREFIXERtoggle in the generatedpostcss.config.jsto support the newpreviewrun mode. (#6663) - Add a staged
Plugin.register_routehook withadd_pageandhas_app_pagecapabilities that runs once per app before its first compilation, andrx.plugins.get_plugin()to look up the configured plugin instance by type (raisingConfigErroron ambiguous matches). (#6728)
Bug Fixes
- Preserve runtime value types for unannotated
@rx.memocomponent parameters. (#6659) - Compare timezone-aware datetime Vars by their instants instead of their serialized UTC offsets. (#6767)
- In python 3.13+, the EventProcessor loop was not catching asyncio.QueueShutdown resulting in uncaught exceptions during shutdown. (#6773)
- Prevented edits to unloaded routes from poisoning React Router's browser-side HMR queue and blocking all later hot updates until a full page reload. (#6774)
- Drain queued same-token events during graceful event processor shutdown. (#6791)
- Custom attributes on a
Fieldare now carried onto the rebuilt field by reference instead of deep copy, so stateful callable markers keep their identity and markers holding non-copyable values (locks, clients) no longer crash state class creation. (#6809)
Miscellaneous
- Bumped bundled frontend dependency pins to their current releases:
react/react-dom: 19.2.6 → 19.2.8react-router,react-router-dom,@react-router/node,@react-router/dev,@react-router/fs-routes: 7.15.0 → 7.18.2isbot: 5.1.40 → 5.2.1universal-cookie: 7.2.2 → 8.1.2postcss: 8.5.14 → 8.5.23autoprefixer: 10.5.0 → 10.5.4@tailwindcss/typography: 0.5.19 → 0.5.20- Bun: 1.3.13 → 1.3.14
- Also raised the
richupper bound to<16(adopting rich 15). Replaced the now-redundantcookiepackage.jsonoverride (universal-cookie8 andreact-routerresolvecookieto 1.x on their own) with apostcssoverride pinning it to 8.5.23 so transitive resolutions stay on a patched release (>= 8.5.18) for a security advisory. - (#6678)
- Remove the unused
REFLEX_USE_TURBOPACKenvironment variable. Turbopack is a Next.js bundler; the flag has had no effect since Reflex moved to React Router and Vite in 0.8. (#6803)
v0.9.7 (2026-07-15)
Deprecations
ArrayVar.foreachis deprecated; useArrayVar.mapinstead. (#6701)
Features
ArrayVargainedmap,filter,reduce, andflat_mapoperations, andStringVar.stripnow accepts acharsargument alongside newlstrip/rstripmethods. (#6701)- Added
default_color_modetorx.Config("system","light", or"dark", also settable viaREFLEX_DEFAULT_COLOR_MODE) and moved the sharedLiteralColorModetype and color-mode string constants intoreflex_base.constants. This lets apps set the initial color mode without depending on the Radix themes appearance prop. (#6716) @rx.memonow accepts awrapper=argument controlling the JS function that wraps the compiled component definition: keep the default Reactmemo, pass a custom functionVar(e.g. anrx.vars.FunctionStringVarcarrying its own imports), or passwrapper=Noneto export the bare function component. (#6730)- Added
frozen_lockfiletorx.Config(defaultTrue, also settable viaREFLEX_FROZEN_LOCKFILE), controlling whether the frontend package manager runs in lockfile-enforcing mode. Reflex still creates, manages, and syncs the lockfile regardless; the option only controls whether a lockfile/package.jsonmismatch is treated as an error. (#6763)
Bug Fixes
- Custom attributes set on a
Fieldare now preserved (deep-copied) when the state metaclass rebuilds fields, instead of being silently discarded. The reservedannotationattribute is never carried over so rebuilt fields are not misidentified as pydantic fields. (#6726) - Fix
_get_all_hooks_internalmutating each component's cached internal hooks with its descendants' hooks, which made memo tag hashes order-dependent and duplicated hooks into memo bodies. (#6741)
Performance
- Cache framework-path checks in
console.deprecate's call-stack walk, making repeat calls ~150x faster (deprecated attributes on hot paths, likeRouterData.page, no longer cost multiple milliseconds per access). (#6736) - Event chaining (
yield OtherState.handler(rows)) no longer deep-copies payload values that are not attached to any state: only state-boundMutableProxysubtrees are copied, making proxy-free payloads ~5x faster to chain. (#6739) Var.to()andVar.guess_type()resolve their target Var subclass through cached registry lookups instead of scanning the full registry withsafe_issubclasson every call (~70% of the cost of constructing a var operation). (#6742)
v0.9.6.post1 (2026-06-26)
Features
- Added the
REFLEX_EXTRA_PLUGINSenvironment variable, a colon-separated list of fully qualified plugin import paths appended to the config'spluginslist. UnlikeREFLEX_PLUGINS, which replaces the list entirely, this preserves plugins configured inrxconfig.py; an entry is skipped when a plugin of the same type is already present or when its type is listed indisable_plugins. (#6685)
Bug Fixes
- Stop warning when a non-built-in plugin is listed in
disable_plugins, so config can opt out of an env-provided plugin without a spurious warning. (#6685) - Improve error message when plugin spec from environment cannot be used. (#6685)
v0.9.6 (2026-06-25)
Features
StringVarnow includeslstripandrstripmethods. Thestripmethod now accepts an optionalcharsargument for consistency with Python’s str API. (#5417)- Added
reflex_base.utils.memo_paths, which translates a memo's Python source module into the mirrored.web/app_components/JSX path and$/...library specifier used by the compiler. The memo component and compiler plugin now route each memo's compiled output through these helpers so it lands alongside its source module's layout, falling back to the per-nameutils/components/<name>path when the module can't be mirrored. The helpers also derive a per-module-unique JS symbol for each mirrored memo, and the memo registry is keyed by(name, source module)so same-named memos defined in different modules coexist instead of colliding. (#6457) ObjectVarattribute access now resolvesHybridPropertydescriptors defined on the underlying type, evaluating the property's frontend logic with the object var substituted asself.HybridPropertymoved toreflex_base.vars.hybrid_property(still available asrx._x.hybrid_property). (#6617)- Add
AgentsMdconstants (canonical URL, managed-section markers, andCLAUDE.mdbridge) supportingreflex initAGENTS.md generation. (#6620) - Added
HybridPropertyError, raised when a hybrid property's frontend logic accesses a backend (underscore-prefixed) var on a state while building its frontend var. (#6621) package_json_templateaccepts**additional_keysto include extra fields (e.g.name,packageManager,engines) in the rendered package.json. (#6658)
Bug Fixes
- Preserve extra bound event arguments when
rx.upload_filesis used in an upload handler. (#5290) - Avoid re-entering config loading when a
Statesubclass is defined inrxconfig.py. (#6662) - Bump the bundled
vitedev dependency to 8.0.16, fixing aserver.fs.denybypass on Windows alternate paths (CVE-2026-53571) in the dev server of generated apps. (#6665) pyi_hashes.jsonentries are now computed from the final.pyicontent afterruff format/ruff check --fixpost-processing, instead of the intermediate generator output. A pyi_generator change that only affects pre-format output no longer flags hash changes for stubs whose final content is identical.
Miscellaneous
Componentgained a private_get_tag_name()helper returning the JS expression that references the component's tag (quoted for global-scope DOM tags without a library);Component._renderandDebounceInputnow share it instead of duplicating the quoting logic. (#6637)
v0.9.5 (2026-06-10)
Features
- Event-argument type checking now treats a mapping-style payload as compatible with a
TypedDict-annotated callback parameter, scoped narrowly toon_submittriggers whose payload is aMapping[str, ...]so unrelated mapping events are unaffected. Adds theFORM_SUBMIT_MAPPINGtype var (exposed on the event namespace andpyi_generator's default imports) and aComponent._is_form_controlclass marker that a component sets to declare it contributes a named field to form submission data. (#6301) VarDatagained anapp_wrapsfield so aVarcan declare the app-level wrapper components it requires; the compiler injects them around the app root, deduped by(priority, tag). This is how the state and event-loop providers now reach the React tree, since event dispatch reachesaddEventsvia a module-level import (Imports.EVENTS) rather than a hoisted hook. The still-reactiveconnectErrorsvalue moves to its ownCONNECT_ERRORSimport/hook, andComponentdeep copies now drop the render cache so compile-time clones (e.g. the app-root wrapper chain) render their mutated children. (#6447)- Added a
hydrate_fallbackconfig option (settable via theREFLEX_HYDRATE_FALLBACKenvironment variable), a dotted import path to a callable returning the component shown while the page is hydrating. The app root template now emits a React RouterHydrateFallbackexport when a fallback is provided, and the import-path resolution shared withextra_overlay_functionresolves nested module paths correctly. (#6630) - Added the
REFLEX_HOT_RELOAD_OVERRIDE_PATHSenvironment variable, a colon-separated list of paths that, when set, fully replaces the paths watched for hot reload in dev mode. (#6639)
Bug Fixes
- The
reflex_base.utils.pyi_generatorbuild-hook entrypoint no longer rewritespyi_hashes.json: it only emits the.pyistubs bundled in the wheel, so building a component package (or the build triggered byuv sync) no longer wipes the hash registry down to a single package's entries. The scanner also tolerates source-less modules (e.g. an empty__init__.py) instead of raisingOSErroron Python < 3.13. (#6614) - Fixed
State.router.urlreflecting a stale query string after the URL was changed withwindow.history.replaceState/pushState(e.g. fromrx.call_script). React Router's location does not observe direct history manipulation, so the query and hash are now read from the livewindow.locationwhen buildingrouter_data, and the next event sent to the backend reports the correct URL (the path stays basename-relative sofrontend_pathis not applied twice; embedded apps keep using the in-widget memory router). A direct history mutation is intentionally not a navigation and does not itself emit an event — userx.redirect(..., replace=True)when you need the URL change to update the router reactively and triggeron_load. (#6625) - pyi_generator no longer includes underscore-prefixed props in generated .pyi files. (#6628)
- Frontend-only events (e.g.
rx.toast,rx.redirect) returned from a middleware'spreprocessare now emitted to the client instead of being enqueued on the backend event queue, where they had no registered handler and raisedKeyError. The frontend/backend split that already applied to handler-yielded events is now shared via a_route_eventshelper and applied to middleware-preprocess updates too. (#6644)
Performance
- Speed up component creation by resolving field defaults lazily (via class-level descriptors) instead of eagerly on every instance, caching each component class's event triggers, and memoizing
to_camel_case. (#6576)
v0.9.4 (2026-06-03)
Deprecations
- Component-returning
@rx.memoagain acceptskeywithout anrx.RestProp(with a deprecation warning), sorx.foreachcall sites that set the reactkeykeep working; this fallback is removed in 1.0. Other base props (id,class_name,style,custom_attrs,ref) and identity fields liketag/librarystill raise — declare anrx.RestPropto forward them.
Features
- Dependency tracking now follows through hybrid properties, so computed vars that read a
hybrid_propertycorrectly recompute when the underlying state vars change. (#3806) - The component memo implementation now lives in
reflex_base.components.memoand is exported asrx.memo. AddedEMPTY_VAR_STRandEMPTY_VAR_INTsentinel vars as memo-friendly defaults. (#6517)
Bug Fixes
- Pin
[email protected]via package overrides to work around upstream breakage in recharts and es-toolkit. (#6570) - Unpin
[email protected]via package overrides and bump vite to 8.0.14 to work around upstream breakage in recharts and es-toolkit. (#6571)
Miscellaneous
- Removed the
Templates.CHOOSE_TEMPLATESandTemplates.REFLEX_TEMPLATES_URLconstants, which supported the now-removed open-source templatesreflex initoption. (#6592)