Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
4bb5b75
Materialize changelogs for reflex@0.10.0a1, reflex-base@0.10.0a1, ref…
github-actions[bot] Oct 5, 2026
59c675a
Materialize changelogs for reflex-components-core@0.10.0a1 (new-prere…
github-actions[bot] Oct 5, 2026
23a3b6e
Document class-level backend var access returning Field as breaking
claude Oct 6, 2026
6778caf
Merge remote-tracking branch 'origin/main' into r/pre-2026.10.05-3737…
masenf Oct 6, 2026
052ca91
Merge remote-tracking branch 'origin/main' into r/pre-2026.10.05-3737…
masenf Oct 7, 2026
bf12bd4
Prepare Python 3.11 floors and phased 0.10 prereleases (#7464)
masenf Oct 7, 2026
2d06383
Materialize changelogs for reflex-base@0.10.0a2 (continued-prerelease)
github-actions[bot] Oct 7, 2026
a0cb494
Materialize changelogs for reflex-build-sdk@0.1.0a1, reflex-component…
github-actions[bot] Oct 7, 2026
7aa2363
Materialize changelogs for reflex-components-sonner@0.10.0a1, reflex-…
github-actions[bot] Oct 7, 2026
ff2995c
Materialize changelogs for reflex-components-core@0.10.0a2, reflex-co…
github-actions[bot] Oct 7, 2026
15057ac
Materialize changelogs for reflex-components-dataeditor@0.10.0a1, ref…
github-actions[bot] Oct 7, 2026
da79305
Materialize changelogs for reflex-components-plotly@0.10.0a2, reflex-…
github-actions[bot] Oct 7, 2026
cf7610f
Materialize changelogs for reflex-components-code@0.10.0a2 (continued…
github-actions[bot] Oct 7, 2026
a8a21f5
Materialize changelogs for reflex-components-markdown@0.10.0a2 (conti…
github-actions[bot] Oct 7, 2026
82ba92e
Materialize changelogs for reflex@0.10.0a2 (continued-prerelease)
github-actions[bot] Oct 7, 2026
3a211ff
Materialize changelogs for reflex-docgen@0.10.0a2 (continued-prerelease)
github-actions[bot] Oct 7, 2026
e211584
Add news fragment for reflex-otel's Python 3.10 drop
claude Oct 7, 2026
3f81026
Require reflex-base 0.10 in reflex-otel
claude Oct 7, 2026
2742de4
Materialize changelogs for hatch-reflex-pyi@0.10.0a1, reflex-otel@0.2…
github-actions[bot] Oct 7, 2026
e1c0c88
Merge remote-tracking branch 'origin/main' into r/pre-2026.10.06-3757…
masenf Oct 7, 2026
555b667
Materialize changelogs for reflex@0.10.0a3, reflex-base@0.10.0a3 (con…
github-actions[bot] Oct 7, 2026
a8c5058
Merge remote-tracking branch 'origin/main' into r/pre-2026.10.06-3757…
claude Oct 8, 2026
f6e070e
Materialize changelogs for reflex@0.10.0a4, reflex-base@0.10.0a4 (con…
github-actions[bot] Oct 8, 2026
cc83bc0
Merge remote-tracking branch 'origin/main' into r/pre-2026.10.06-3757…
claude Oct 8, 2026
57aee8c
Materialize changelogs for reflex@0.10.0a5, reflex-base@0.10.0a5 (con…
github-actions[bot] Oct 8, 2026
dac32ff
Consolidate 0.10 pre-release changelogs into final release notes
claude Oct 8, 2026
80d3678
Document deep-copied mutable state var defaults as a breaking change
claude Oct 8, 2026
f74469d
Document deep-copied mutable defaults in the 0.10 upgrade guide
claude Oct 8, 2026
012b0eb
Add the towncrier start marker to the hatch-reflex-pyi changelog
claude Oct 8, 2026
f05eeb8
Restore the sync=True LocalStorage cross-tab fix to the reflex-base r…
claude Oct 8, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions .github/workflows/dispatch_release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ name: Dispatch release
# Selecting no package auto-selects the ones to release: those with pending
# news fragments — or, for release-from-prerelease, those whose changelog is
# topped by an alpha (the train's fragments are already consumed). Lockstep
# groups share one checkbox: their members only ever release together.
# groups without publish-last share one checkbox: their members only ever
# release together. With publish-last, each member has its own.
#
# Materializing also lifts each released package's unshippable dependency
# floors — a *.dev pin, and a prerelease pin when the version is final — to the
Expand Down Expand Up @@ -75,13 +76,17 @@ on:
- release-minor
- release-major
reflex:
description: "reflex (with reflex-base)"
description: "reflex"
type: boolean
default: false
hatch_reflex_pyi:
description: "hatch-reflex-pyi"
type: boolean
default: false
reflex_base:
description: "reflex-base"
type: boolean
default: false
reflex_build_sdk:
description: "reflex-build-sdk"
type: boolean
Expand Down Expand Up @@ -186,8 +191,9 @@ jobs:
# Each unselected package contributes an empty string, so selecting
# nothing leaves PACKAGES blank and the planner auto-selects.
PACKAGES: >-
${{ inputs.reflex && 'reflex,reflex-base' || '' }}
${{ inputs.reflex && 'reflex' || '' }}
${{ inputs.hatch_reflex_pyi && 'hatch-reflex-pyi' || '' }}
${{ inputs.reflex_base && 'reflex-base' || '' }}
${{ inputs.reflex_build_sdk && 'reflex-build-sdk' || '' }}
${{ inputs.reflex_components_code && 'reflex-components-code' || '' }}
${{ inputs.reflex_components_core && 'reflex-components-core' || '' }}
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/release_from_changelog.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ name: Release from changelog
# r/pre-* or r/hotfix/** (the Dispatch release workflow
# materializes alphas on r/pre-* branches).
#
# Lockstep packages that pin their siblings exactly publish strictly after
# every other package in the batch; detect fails closed if a lockstep
# invariant is broken.
# Lockstep packages that pin their siblings exactly publish in a second leg,
# after the rest of the batch, and only once the siblings they pin are tagged;
# detect fails closed if a lockstep invariant is broken.
#
# workflow_dispatch exists so the Dispatch release workflow can trigger a
# check on a branch it just pushed (pushes made with GITHUB_TOKEN do not fire
Expand Down
89 changes: 89 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,92 @@
## v0.10.0a5 (2026-10-08)

### 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](https://github.com/reflex-dev/reflex/issues/7449))
- `reflex[db]` allows SQLModel 0.0.45 and later, which stores a plain `datetime` field as UTC: writes and filters need timezone-aware values and reads return aware ones. To keep naive datetimes, declare the field with `sa_type=DateTime(timezone=False)` or pin `sqlmodel<0.0.45`, as described in [Datetimes and SQLModel upgrades](https://reflex.dev/docs/database/tables/#datetimes-and-sqlmodel-upgrades). ([#7462](https://github.com/reflex-dev/reflex/issues/7462))

#### State

- Reading a backend var on a state class (`State._items`) now returns its `Field` descriptor instead of the var's default value. Passing it to a component, which used to bake the default into the frontend, now raises `ChildrenTypeError` as a child or `TypeError: Unsupported type <class 'reflex_base.vars.base.Field'> for LiteralVar.` as a prop. To keep baking in the default, pass `State._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](https://reflex.dev/docs/changelog/upgrading/upgrading-to-0-10/#reading-a-backend-var-on-a-state-class). ([#7312](https://github.com/reflex-dev/reflex/issues/7312))
- Assigning a state var through its state class, such as `State.count = 10`, raises `TypeError` instead of replacing the var; so do pytest's `monkeypatch.setattr` and `unittest.mock.patch.object` on a var. Change a default with `State.__fields__["count"].set_default(10)`, patch the field in tests, and declare class-level configuration as `ClassVar`. See [Upgrading to Reflex 0.10](https://reflex.dev/docs/changelog/upgrading/upgrading-to-0-10/#assigning-a-state-var-through-its-class). ([#7516](https://github.com/reflex-dev/reflex/issues/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 the `class` statement, such as `options: list[str] = OPTIONS` followed by `OPTIONS.append(...)`, now starts every session empty: fill it before the class is defined, or declare `default_factory=lambda: list(OPTIONS)`. ([#7519](https://github.com/reflex-dev/reflex/issues/7519))
- In a background task, calling a state-modifying handler inherited from a parent state, or writing an inherited var, outside `async with self` now raises `ImmutableStateError` like any other state change. See [Upgrading to Reflex 0.10](https://reflex.dev/docs/changelog/upgrading/upgrading-to-0-10/#calling-inherited-handlers-from-background-tasks). ([#7312](https://github.com/reflex-dev/reflex/issues/7312))
- Assigning an undeclared state attribute still raises `SetUndefinedStateVarError` outside of prod mode, but no longer in prod. The internal class maps `backend_vars`, `inherited_vars` and `inherited_backend_vars`, `get_skip_vars()` and the instance `_backend_vars` are removed: use `get_fields()`, whose fields know the state they belong to. ([#7312](https://github.com/reflex-dev/reflex/issues/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](https://github.com/reflex-dev/reflex/issues/7494))

#### CLI

- Remove the `reflex component` CLI (`init`, `build`, `share`, `install`) and the `CustomComponents` constants; running `reflex component` now 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](https://github.com/reflex-dev/component-template), which builds, tests, and publishes them with standard Python tooling. ([#6425](https://github.com/reflex-dev/reflex/issues/6425), [#7497](https://github.com/reflex-dev/reflex/issues/7497))

### Deprecations

- Deprecate `State.router.headers.cookie` and `State.router.headers["cookie"]` in components; both now render an empty string. Use `rx.Cookie` for cookies that need to be accessible to the frontend. ([#7360](https://github.com/reflex-dev/reflex/issues/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 `BaseVarShadowsInheritedVarError` or `ComputedVarShadowsBaseVarsError`. A dynamic route arg only conflicts with a var of the state it is installed on. ([#7312](https://github.com/reflex-dev/reflex/issues/7312))
- The duration settings read by the app and the state managers take a unit suffix: `SQLALCHEMY_POOL_TIMEOUT`, `REFLEX_SOCKET_INTERVAL` and `REFLEX_SOCKET_TIMEOUT` accept values such as `2m`, and `REFLEX_AUTO_RELOAD_COOLDOWN`, `REFLEX_OPLOCK_HOLD_TIME` and `REFLEX_STATE_MANAGER_DISK_DEBOUNCE` replace the `_MS`/`_SECONDS` names, which still work with a deprecation warning until 1.0. A bare number is read as seconds. ([#7138](https://github.com/reflex-dev/reflex/issues/7138))
Comment thread
masenf marked this conversation as resolved.
Resolved
- Set `REFLEX_REDIS_MAX_CONNECTIONS` to 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 to `REFLEX_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](https://github.com/reflex-dev/reflex/issues/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](https://github.com/reflex-dev/reflex/issues/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 a `default_factory` compiles with the storage options the factory produces, and `reset()` restores the factory's value. ([#7461](https://github.com/reflex-dev/reflex/issues/7461))
Comment thread
masenf marked this conversation as resolved.
Resolved
- 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 raises `SetUndefinedStateVarError` in dev mode. Private names not explicitly declared with `rx.field()` are plain Python attributes: vars do not update in response to them. ([#7465](https://github.com/reflex-dev/reflex/issues/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](https://github.com/reflex-dev/reflex/issues/7312))
- A page URL with a `self` query parameter (e.g. `/post?self=1`), or a request header named `self`, no longer crashes router data parsing and leaves the page unhydrated. ([#7324](https://github.com/reflex-dev/reflex/issues/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](https://github.com/reflex-dev/reflex/issues/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](https://github.com/reflex-dev/reflex/issues/7318))
- Fix a race with `REFLEX_OPLOCK_ENABLED` where 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](https://github.com/reflex-dev/reflex/issues/7372))
- Fix `TypeError: refs._client_state_set... is not a function` when a component sets a global `rx._x.client_state` value before any component reading `.value` has mounted, such as when the reader sits behind an `rx.cond`. ([#7286](https://github.com/reflex-dev/reflex/issues/7286))
- Preserve ID-based form controls through automatic memoization while excluding IDs on non-controls from submissions. ([#7227](https://github.com/reflex-dev/reflex/issues/7227))

#### Database

- The `db` extra now installs `greenlet`, which SQLAlchemy 2.1 no longer pulls in on its own, so `rx.Model`, `rx.session()` and the `reflex db` commands work on a fresh `pip install reflex[db]` instead of failing with `ImportError: The SQLAlchemy asyncio module requires that the Python 'greenlet' library is installed`. ([#7466](https://github.com/reflex-dev/reflex/issues/7466))
- Fixed `reflex db makemigrations`/`migrate` crashing with `CompileError` when autogenerating a migration that adds a column with a callable default (e.g. `default_factory=datetime.now` or `default=uuid.uuid4`) to an existing table; callable defaults are now evaluated before being carried as a SQL `server_default`. ([#6706](https://github.com/reflex-dev/reflex/issues/6706))
- `reflex db` commands only require `sqlalchemy` and `alembic`, so apps that use plain SQLAlchemy models without `sqlmodel` can run migrations again. ([#7322](https://github.com/reflex-dev/reflex/issues/7322))
- `reflex db` commands run without the `db` extra installed now exit with the "pip install reflex[db]" message instead of a raw traceback. ([#7259](https://github.com/reflex-dev/reflex/issues/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](https://github.com/reflex-dev/reflex/issues/6996))
- `reflex run` now 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](https://github.com/reflex-dev/reflex/issues/7328))
- `reflex run --json` now emits every output line as a JSON record: `print()` output from the app, subprocess output, and worker tracebacks (as one record with an `exception` field) no longer break the JSON-lines stream. ([#7350](https://github.com/reflex-dev/reflex/issues/7350))
- Fix stylesheet edits in `assets/` not applying in dev mode until a manual reload. ([#7317](https://github.com/reflex-dev/reflex/issues/7317))
- Avoid reinstalling frontend packages on every compile or hot reload when bun or npm only changes the formatting of `package.json`. ([#7236](https://github.com/reflex-dev/reflex/issues/7236))
- When a project keeps using npm because `reflex.lock/` only has `package-lock.json` (for example after a run with `REFLEX_USE_NPM=1`), Reflex now logs why and how to switch back to bun with `REFLEX_USE_NPM=0`. ([#7093](https://github.com/reflex-dev/reflex/issues/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](https://github.com/reflex-dev/reflex/issues/7210))
- An editable install (`uv sync`, `pip install -e .`) no longer overwrites `.pyi` stubs that the checkout already has. A checkout missing any of them still gets them generated. ([#7303](https://github.com/reflex-dev/reflex/issues/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](https://github.com/reflex-dev/reflex/issues/7348))
- Stop logging `asyncio.CancelledError: lifespan_cleanup` as an error when a running coroutine lifespan task is cancelled at backend shutdown or hot reload. ([#7392](https://github.com/reflex-dev/reflex/issues/7392))

#### Testing

- `AppHarness` now 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 using `rx.dynamic`) no longer breaks the next app started in the same test process. ([#7359](https://github.com/reflex-dev/reflex/issues/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](https://github.com/reflex-dev/reflex/issues/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](https://github.com/reflex-dev/reflex/issues/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](https://github.com/reflex-dev/reflex/issues/7370))
- Apps that define an `rx.SharedState` no longer pay extra per-event work for events that change no shared state. ([#7237](https://github.com/reflex-dev/reflex/issues/7237))
- Compile memoized components faster by reusing unchanged memo bodies instead of rendering them again. ([#7123](https://github.com/reflex-dev/reflex/issues/7123))

### Documentation

- Add an [Upgrading to Reflex 0.10](https://reflex.dev/docs/changelog/upgrading/upgrading-to-0-10/) guide covering the changes most likely to break an existing app, with the fix for each. ([#7496](https://github.com/reflex-dev/reflex/issues/7496))
- Document the dict form for `rx.toast` `action` and `cancel` props instead of referencing the non-public `ToastAction` class. ([#7327](https://github.com/reflex-dev/reflex/issues/7327))

### Miscellaneous

- Allow wrapt 2.4 and 2.5. ([#7424](https://github.com/reflex-dev/reflex/issues/7424))


## v0.9.12 (2026-09-21)

### Breaking Changes
Expand Down
13 changes: 7 additions & 6 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,16 +115,17 @@ a release branch — never by tagging manually. The pieces:
`release-from-prerelease` collapses the accumulated alpha sections into
one final-version section — alpha headings never ship in a final
changelog.
- `reflex` and `reflex-base` are a lockstep pair and share one checkbox:
selecting it releases both at the same version. The checkboxes are
generated from the package list, so adding or removing a package means
re-running `uv run reflex-release sync`.
- `reflex` and `reflex-base` are a lockstep pair with separate checkboxes:
`reflex-base` releases on its own, while `reflex` needs `reflex-base`
tagged at the same version — reusing an existing tag or releasing base
alongside. The checkboxes are generated from the package list, so adding
or removing a package means re-running `uv run reflex-release sync`.
2. **Release from changelog** (`release_from_changelog.yml`) runs on every push
to `main`, `r/pre-*`, and `r/hotfix/**`: any package whose newest changelog
version has no git tag gets built and queued for publishing. Final
(non-alpha) versions only publish from `main` or `r/hotfix/**`; alphas only
from `r/pre-*`/`r/hotfix/**`. `reflex` and `reflex-base` are checked as a
lockstep pair and `reflex` publishes only after the rest of the batch.
from `r/pre-*`/`r/hotfix/**`. `reflex` publishes only after the rest of the
batch, once its `reflex-base` tag exists.
3. **Publish to PyPI** (`publish.yml`, also manually dispatchable with a
package + version) validates and builds without privileges, then **waits
for a human to approve the `pypi` environment deployment** — every upload,
Expand Down
Loading
Loading