Skip to content

Document hybrid_property in the Vars section#6622

Open
masenf wants to merge 7 commits into
claude/hybrid-property-backend-var-guardfrom
claude/hybrid-property-docs
Open

Document hybrid_property in the Vars section#6622
masenf wants to merge 7 commits into
claude/hybrid-property-backend-var-guardfrom
claude/hybrid-property-docs

Conversation

@masenf

@masenf masenf commented Jun 6, 2026

Copy link
Copy Markdown
Collaborator

Type of change

  • Documentation

Note

Stacked on #6621 β€” base branch is claude/hybrid-property-backend-var-guard. This PR contains only the docs; CI won't run until the base is main.

Description

Adds a Hybrid Properties page to the Vars section (docs/vars/hybrid_properties.md) and registers it in the sidebar right after Computed Vars.

The page contrasts hybrid properties with computed vars β€” the key distinction being that a computed var computes on the server and caches/sends the result (duplicating data), whereas a hybrid property compiles to a client-side expression over existing vars and sends nothing extra, making it a convenient way to reformat frontend data when needed. It also covers:

  • a live demo deriving full_name from first_name/last_name
  • the separate frontend implementation via @<name>.var
  • the backend-var restriction (can't reference _-prefixed vars in frontend logic)
  • nested-object usage (dataclass / pydantic / SQLAlchemy via an object var)

Changes

  • docs/vars/hybrid_properties.md β€” new page
  • docs/app/reflex_docs/templates/docpage/sidebar/sidebar_items/learn.py β€” sidebar entry

https://claude.ai/code/session_01DKFiYGnWRQG8wMNKFW7obm


Generated by Claude Code

claude added 7 commits June 5, 2026 20:25
`rx._x.hybrid_property` previously only resolved as a frontend var when
accessed directly on a `State` class. Accessing it through an object var
(e.g. `State.info.a_b` where `info` is a dataclass, pydantic model or
SQLAlchemy model) raised `VarAttributeError`.

`ObjectVar.__getattr__` now detects a `HybridProperty` defined on the
underlying type and evaluates its frontend logic with the object var
substituted as `self`, so it renders with the same Var-access semantics
as accessing the hybrid property directly on the state. This works
uniformly across bare classes, pydantic models, SQLAlchemy models and
dataclasses, since they are all treated as object vars.

`HybridProperty` moved to `reflex_base.vars.hybrid_property` (so the var
system can reference it without an inverted dependency) and is still
re-exported from `reflex.experimental.hybrid_property`.

Fixes #6617

https://claude.ai/code/session_01DKFiYGnWRQG8wMNKFW7obm
Import `hybrid_property` directly from `reflex_base.vars.hybrid_property`
in `reflex.experimental.__init__` instead of going through a one-line
re-export module. `from reflex.experimental import hybrid_property` and
`rx._x.hybrid_property` are unchanged.

https://claude.ai/code/session_01DKFiYGnWRQG8wMNKFW7obm
…non-states

HybridProperty.__get__ produced a frontend var for any class-level access,
which only makes sense on a state (whose class attributes are vars). On a
plain class accessed directly β€” e.g. `Info.a_b` on a dataclass, not through
an object var β€” it ran the getter with the class as `self`, raising
AttributeError (no field default) or returning a value built from class
defaults. It now returns the descriptor itself, like a normal property.

Var access through an object var (`State.info.a_b`) is unaffected: it is
resolved by ObjectVar.__getattr__ via _get_var, not __get__.

https://claude.ai/code/session_01DKFiYGnWRQG8wMNKFW7obm
ObjectVar.__getattr__ ran inspect.getattr_static on every attribute access to
detect a HybridProperty on the underlying type β€” a pure-Python MRO walk on a hot
path, ~15x slower than getattr for ordinary field access. Now that
HybridProperty.__get__ returns the descriptor itself for non-state class access,
a plain getattr surfaces it directly, so the static lookup is no longer needed.

https://claude.ai/code/session_01DKFiYGnWRQG8wMNKFW7obm
A hybrid property's frontend logic (its getter, or a custom @<name>.var
function) runs with the state class as `self` when building the frontend
var. Reading a backend (underscore-prefixed) var there previously baked
the var's class-level default into the frontend as a frozen literal β€” a
silent leak that never updates and is not reactive.

HybridProperty._get_var now wraps a state owner in a guard that raises
HybridPropertyError when a backend var is accessed, with a message that
points at the misuse in the user's getter/var function. Object-var owners
(nested dataclass/model access) have no backend vars and are unchanged.
https://claude.ai/code/session_01DKFiYGnWRQG8wMNKFW7obm
Add docs/vars/hybrid_properties.md, contrasting hybrid properties with
computed vars: a computed var computes on the server and caches/sends the
result (duplicating data), whereas a hybrid property compiles to a
client-side expression over existing vars and sends nothing extra β€” a
convenient way to reformat frontend data when needed. Covers the custom
@<name>.var frontend implementation, the backend-var restriction, and
nested-object usage, and registers the page in the sidebar after
computed vars.

https://claude.ai/code/session_01DKFiYGnWRQG8wMNKFW7obm
@masenf masenf requested review from a team and Alek99 as code owners June 6, 2026 00:19
@codspeed-hq

codspeed-hq Bot commented Jun 6, 2026

Copy link
Copy Markdown

Merging this PR will degrade performance by 15.79%

⚠️ Different runtime environments detected

Some benchmarks with significant performance changes were compared across different runtime environments,
which may affect the accuracy of the results.

Open the report in CodSpeed to investigate

❌ 10 regressed benchmarks
βœ… 16 untouched benchmarks
⏩ 8 skipped benchmarks1

Warning

Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Benchmark BASE HEAD Efficiency
❌ test_evaluate_page[_complicated_page] 27.3 ms 38.7 ms -29.52%
❌ test_evaluate_page_with_hooks[_complicated_page] 28 ms 39.5 ms -29.11%
❌ test_evaluate_page[_stateful_page] 5.1 ms 6.5 ms -22.37%
❌ test_evaluate_page_with_hooks[_stateful_page] 5.8 ms 7.4 ms -21.27%
❌ test_compile_all_artifacts[_complicated_page] 64.3 ms 77.5 ms -17.02%
❌ test_compile_all_artifacts[_stateful_page] 27.2 ms 29.9 ms -9.03%
❌ test_compile_page[_complicated_page] 115.7 ms 127.1 ms -9%
❌ test_compile_page[_stateful_page] 30.7 ms 33.3 ms -7.83%
❌ test_compile_page_full_context[_complicated_page] 136.4 ms 141.7 ms -3.7%
❌ test_compile_page_full_context[_stateful_page] 35.8 ms 37.1 ms -3.69%

Tip

Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.


Comparing claude/hybrid-property-docs (52d6b4d) with claude/hybrid-property-backend-var-guard (c44fc0a)

Open in CodSpeed

Footnotes

  1. 8 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports. ↩

@greptile-apps

greptile-apps Bot commented Jun 6, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

Adds a new Hybrid Properties page to the Vars section of the docs and registers it in the sidebar. The page explains the distinction between hybrid properties (client-side expression compiled from existing vars, no extra data sent) and computed vars (server-side evaluation with cached result), backed by live editable demos.

  • docs/vars/hybrid_properties.md β€” new page covering basic usage, frontend/backend comparison table, separate @<name>.var override pattern, and nested-object (dataclass/Pydantic/SQLAlchemy) usage.
  • docs/app/reflex_docs/templates/docpage/sidebar/sidebar_items/learn.py β€” one-line sidebar entry inserting the new page between Computed Vars and Var Operations.

Confidence Score: 5/5

Safe to merge β€” purely additive documentation with no code changes to the framework itself.

The two changed files are a new Markdown doc and a one-line sidebar addition. The examples in the doc were cross-checked against the implementation in hybrid_property.py and the integration/unit tests β€” the f-string-with-Var pattern works correctly via Var.format's tagged-string encoding, rx._x.hybrid_property is confirmed exported, and the sidebar entry is auto-resolved by the dynamic namespace builder in init.py. No factual inaccuracies or broken patterns found.

No files require special attention.

Important Files Changed

Filename Overview
docs/vars/hybrid_properties.md New docs page introducing hybrid_property with live demos, a comparison table, separate-frontend-impl section, and nested-object example. Examples are technically correct β€” the f-string-with-Var mechanism is confirmed by the tagged-string encoding in Var.format.
docs/app/reflex_docs/templates/docpage/sidebar/sidebar_items/learn.py Adds vars.hybrid_properties to the sidebar between computed_vars and var_operations; the entry is auto-resolved from the new .md file via the dynamic namespace builder in init.py.

Reviews (1): Last reviewed commit: "docs: document hybrid_property in the Va..." | Re-trigger Greptile

@masenf masenf force-pushed the claude/hybrid-property-backend-var-guard branch 2 times, most recently from bff3fa1 to 9c1894f Compare June 19, 2026 16:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants