Refactor sidebar to use reactive Var operations instead of Python conditionals#6528
Conversation
Convert sidebar_comp into an experimental memo component so the rendered sidebar tree compiles to a single React component shared across every docpage. The route-specific url and per-section indices are now passed as runtime props, and the per-page JSX no longer re-inlines the entire sidebar. - Decorate sidebar_comp with @rx._x.memo; type all parameters as rx.Var subclasses (StringVar / ArrayVar) so the memo system can build placeholders and emit JSX with Var operations. - Update helpers (sidebar_leaf_guide, sidebar_leaf, sidebar_item_comp, sidebar_category, create_sidebar_section) to accept Var-typed url and index parameters. - Replace Python conditionals on Var-derived expressions with rx.cond. - Convert index handling (bool(index), index[1:] if open else []) to Var operations (index.length(), rx.cond(is_open, index[1:], []).to(...)). - Express startswith / contains URL checks via StringVar.startswith / StringVar.contains chained with Var __or__ / __and__ / __invert__. - Drop the unused cli_ref_index and width parameters from sidebar_comp; sidebar() still accepts width for callers but no longer threads it through. normalize_url stays in the outer sidebar() since it relies on Python string ops not expressible as Var operations. https://claude.ai/code/session_013mppZsm9QZVz6DTPYokXR2
reduce a TON of duplicate inline styles by memoizing SidebarLeaf into a separate component. Definitely more work that could be done to get the overall code size of the sidebar down a bit more even and reduce re-renders when navigating.
Greptile SummaryThis PR refactors the docs sidebar to replace Python-level
Confidence Score: 4/5The refactor is behaviorally correct and the reactive conversion is well-structured; two dead parameters (one on a public function signature) are the only notable rough edges. The reactive Var operations faithfully replicate the old Python conditionals, link normalization is moved cleanly to post_init, and the memoization boundaries are well-reasoned. The guide_margin_class parameter on sidebar_leaf_outer is declared and required by all callers but never read inside the function body, wasting memo cache slots. The width parameter on the public sidebar() function is still accepted but silently discarded, so any caller passing width= will see no effect. docs/app/reflex_docs/templates/docpage/sidebar/sidebar.py — the unused guide_margin_class parameter on sidebar_leaf_outer and the dangling width parameter on sidebar() both deserve a second look before merge. Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A["sidebar(url, width)\n[plain Python]"] --> B["normalize_url(url)"]
A --> C["calculate_index(section, url)\n17 sections"]
B --> D
C --> D
D["sidebar_comp(url_var, index_vars)\n[@rx._x.memo - React component]"]
D --> E{"url.startswith?"}
E -->|"/hosting/"| F["hosting_categories + hosting_content"]
E -->|"/ai/"| G["ai_builder_categories + rx.cond(is_ai_mcp_or_skills, ...)"]
E -->|else| H{"url.contains?"}
H -->|library or mcp-| I["library_content"]
H -->|api-reference| J["api_reference_content"]
H -->|enterprise| K["enterprise_content"]
H -->|else| L["default_docs_content"]
D --> M["sidebar_item_comp - Python recursion at compile time"]
M --> N["sidebar_leaf @rx._x.memo"]
M --> O["sidebar_leaf_outer @rx._x.memo"]
N --> P["sidebar_leaf_guide - plain helper using rx.cond"]
Reviews (1): Last reviewed commit: "memoize sidebar_leaf separately" | Re-trigger Greptile |
| @rx._x.memo | ||
| def sidebar_leaf_outer( | ||
| item_names: rx.vars.StringVar[str], | ||
| item_link: rx.vars.StringVar[str], | ||
| is_active: rx.vars.BooleanVar, | ||
| guide_margin_class: rx.vars.StringVar[str], | ||
| ) -> rx.Component: | ||
| """Get the leaf node of the sidebar.""" |
There was a problem hiding this comment.
Unused
guide_margin_class parameter in sidebar_leaf_outer
guide_margin_class is declared as a required StringVar parameter on the @rx._x.memo function but is never referenced in the function body. Because the memo system uses all parameter values as cache keys, passing a varying guide_margin_class string (e.g., "ml-[2.5rem]" vs "ml-[3rem]") will produce separate memoized React components with identical output, defeating the purpose of memoization and wasting memory.
| @@ -709,54 +743,40 @@ def sidebar_comp( | |||
|
|
|||
|
|
|||
| def sidebar(url=None, width: str = "100%") -> rx.Component: | |||
There was a problem hiding this comment.
width parameter silently ignored in sidebar
The width parameter is declared on the public sidebar() function and was previously forwarded to sidebar_comp(), but sidebar_comp no longer accepts it. Any caller that passes width= now has that value silently dropped, and the sidebar wrapper's rx.box doesn't apply it either. If width customisation is no longer needed, the parameter should be removed to prevent confusion; if it is still needed, it should be applied to the outer box.
| def sidebar(url=None, width: str = "100%") -> rx.Component: | |
| def sidebar(url=None) -> rx.Component: |
Type of change
Description
This PR refactors the sidebar component to use Reflex's reactive
Varoperations instead of Python-level conditionals, enabling better runtime reactivity and compilation to optimized React code.Key changes:
Type annotations: Updated function signatures to use
rx.vars.StringVar[str],rx.vars.ArrayVar[list[int]], andrx.Var[bool]for reactive parameters instead of plain Python types.Reactive conditionals: Replaced Python
if/elseexpressions withrx.cond()for dynamic UI rendering:sidebar_leaf_guide(): Now usesrx.cond()for conditional renderingsidebar_leaf(): Converted all conditional styling torx.cond()callssidebar_category(): Usesrx.cond()for active state stylingsidebar_comp(): Replaced complex nested Python conditionals with reactiverx.cond()chainsReactive comparisons: Updated logic to use Var operations:
is_opencalculation now usesindex.length() > 0and bitwise&operator.startswith()and.contains()methods on Var objectsMemoization: Added
@rx._x.memodecorator tosidebar_comp()to compile the reactive tree into a single optimized React component that receives runtime props.Separation of concerns: The
sidebar()wrapper function remains a regular Python function that handles non-reactive operations (URL normalization, index calculation), whilesidebar_comp()handles all reactive rendering logic.Removed unused imports: Removed
cli_reffrom imports as it's no longer used.Documentation: Added comprehensive docstrings explaining the memoization strategy and the division between reactive and non-reactive logic.
Motivation
This refactoring enables the sidebar to respond to runtime URL and index changes without re-executing Python code, resulting in better performance and more idiomatic Reflex patterns. The memoized component compiles to a single React component that receives these values as props, rather than recalculating the entire tree on each change.
Testing
Existing sidebar functionality is preserved. The changes are internal refactoring to use reactive patterns rather than Python conditionals. All sidebar rendering behavior remains identical from the user's perspective.
https://claude.ai/code/session_013mppZsm9QZVz6DTPYokXR2