Document hybrid_property in the Vars section#6622
Conversation
`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
Merging this PR will degrade performance by 15.79%
|
| 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)
Footnotes
-
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 SummaryAdds 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.
Confidence Score: 5/5Safe 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
Reviews (1): Last reviewed commit: "docs: document hybrid_property in the Va..." | Re-trigger Greptile |
bff3fa1 to
9c1894f
Compare
Type of change
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 ismain.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:
full_namefromfirst_name/last_name@<name>.var_-prefixed vars in frontend logic)Changes
docs/vars/hybrid_properties.mdβ new pagedocs/app/reflex_docs/templates/docpage/sidebar/sidebar_items/learn.pyβ sidebar entryhttps://claude.ai/code/session_01DKFiYGnWRQG8wMNKFW7obm
Generated by Claude Code