Skip to content

ENG-9676 fix: route memo RestProp CSS props into css instead of dropping them#6605

Open
FarhanAliRaza wants to merge 3 commits into
reflex-dev:mainfrom
FarhanAliRaza:style-fix
Open

ENG-9676 fix: route memo RestProp CSS props into css instead of dropping them#6605
FarhanAliRaza wants to merge 3 commits into
reflex-dev:mainfrom
FarhanAliRaza:style-fix

Conversation

@FarhanAliRaza

@FarhanAliRaza FarhanAliRaza commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

A rx.RestProp only forwarded kwargs that matched a declared field of the target component; any other forwarded prop (e.g. font_weight) was emitted as a raw plain prop the target silently ignored. Track each RestProp target's field names during body evaluation so the call site can fold non-field props into emotion css, matching how a normal component handles unknown kwargs.

All Submissions:

  • Have you followed the guidelines stated in CONTRIBUTING.md file?
  • Have you checked to ensure there aren't any other open Pull Requests for the desired changed?

Type of change

Please delete options that are not relevant.

  • Bug fix (non-breaking change which fixes an issue)

New Feature Submission:

  • Does your submission pass the tests?
  • Have you linted your code locally prior to submission?

Changes To Core Features:

A `rx.RestProp` only forwarded kwargs that matched a declared field of the
target component; any other forwarded prop (e.g. `font_weight`) was emitted
as a raw plain prop the target silently ignored. Track each RestProp target's
field names during body evaluation so the call site can fold non-field props
into emotion `css`, matching how a normal component handles unknown kwargs.
@FarhanAliRaza FarhanAliRaza requested a review from a team as a code owner June 4, 2026 14:37
@FarhanAliRaza FarhanAliRaza changed the title fix: route memo RestProp CSS props into css instead of dropping them ENG-9676 fix: route memo RestProp CSS props into css instead of dropping them Jun 4, 2026
@codspeed-hq

codspeed-hq Bot commented Jun 4, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 26 untouched benchmarks
⏩ 8 skipped benchmarks1


Comparing FarhanAliRaza:style-fix (034e26e) with main (ed0cd15)

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 4, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes a silent-drop bug in @rx.memo components that forward props through rx.RestProp: previously any forwarded kwarg that was not a declared field of the target component was emitted as a raw plain prop and ignored; now those non-field props are folded into emotion css, matching the behaviour of a normal Reflex component.

  • _lift_rest_props now accumulates the declared field names of every component a RestProp is spread onto into a shared set[str] (_rest_target_fields); take_rest uses that set to split rest kwargs into camel-cased plain props (target fields) vs. a single emotion css bundle (everything else).
  • Two new unit tests cover the CSS-routing and the "real prop stays a plain prop" cases; the integration test gains a font_weight="bold" assertion to confirm end-to-end rendering.

Confidence Score: 5/5

The change is well-scoped and correctly routes non-field rest props into emotion css, matching the existing component behaviour.

The core logic in take_rest is straightforward: keys in rest_target_fields get camel-cased plain-prop treatment; everything else is gathered into a Style dict and wrapped in a single css LiteralVar. The set-accumulation in _lift_rest_props is mutated in-place before the call site reads it (forced by rest_target_field_names()), which is the correct evaluation order for both the eager and lazy paths. Tests cover both the new CSS-routing path and the real-target-field guard.

No files require special attention.

Important Files Changed

Filename Overview
packages/reflex-base/src/reflex_base/components/memo.py Core bug fix: take_rest now routes non-target-field props into emotion css; _lift_rest_props accumulates rest-target component field names; rest_target_field_names() forces body evaluation before the call site reads the set.
tests/units/components/test_memo.py Two new unit tests verify CSS props reach emotion css and that genuine target-component fields stay as plain props; existing test assertions updated to match new routing behaviour.
tests/integration/test_memo.py Integration smoke-test extended with font_weight="bold" on a RestProp memo call and a CSS-property assertion to confirm the fix end-to-end.
packages/reflex-base/news/6605.bugfix.md Changelog entry describing the RestProp CSS-routing fix.
pyi_hashes.json Hash updated for the regenerated memo.pyi stub file following the take_rest signature change.

Reviews (2): Last reviewed commit: "Merge remote-tracking branch 'origin/mai..." | Re-trigger Greptile

Comment on lines +1003 to 1006
if css:
rest["css"] = LiteralVar.create(
convert_dict_to_style_and_format_emotion(css)
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 convert_dict_to_style_and_format_emotion can return None

format_as_emotion returns None when its internal emotion_style dict ends up empty after processing (e.g. all entries are filtered or a Var-only style resolves to nothing). Passing None to LiteralVar.create would produce a css={null} prop in the JSX output, which silently drops all emotion styles at runtime. The if css: guard only checks the pre-conversion dict, not the output. A None check on the result would make this safe.

FarhanAliRaza and others added 2 commits June 4, 2026 19:49
Adds the 6605 bugfix changelog entry documenting that `@rx.memo` forwards
undeclared RestProp kwargs into `css`, and regenerates the memo.pyi hash.
@greptile-apps

greptile-apps Bot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Want your agent to iterate on Greptile's feedback? Try greploops.

@masenf masenf left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so one weird thing about this PR:

def test_memo_props_on_create_do_not_become_css():
    """Handle props passed via the RestProp that correspond to kwargs accepted by `create`.

    In this case, the create kwargs are not _real_ props, but they are consumed
    like real props and should NOT become CSS.
    """

    class CustomText(Span):
        @classmethod
        def create(cls, *children, prefix: rx.Var[str] | str = "", **props) -> Component:
            return super().create(prefix, *children, **props)

    @rx.memo
    def styled_text(rest: rx.RestProp) -> rx.Component:
        return CustomText.create("Foo", rest)

    rendered = str(styled_text(prefix="P: ", class_name="c"))
    print(rendered)
    assert "css:" not in rendered
    assert '["prefix"] : "P: "' not in rendered
    assert 'prefix:"P:"' in rendered
    # A genuine base `Component` field stays a normal plain prop.
    assert 'className:"c"' in rendered

I don't think there's actually a way to handle this because rest is not resolvable at compile time and create kwargs are only resolvable at compile time...

Otherwise I generally like the fix here. I wonder if we should audit the component code base in an attempt to remove these kind of "shadow" props that get resolved at compile time?

At the very least, we need to add a line to the documentation for RestProp that there is a limitation that create kwargs cannot be passed via RestProp; only actual component props and css props would be passed through.

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.

@rx.memo with rx.RestProp doesn't handle CSS props correctly

2 participants