Skip to content

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

Open
FarhanAliRaza wants to merge 6 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 6 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 (8197b36) with main (932f20f)

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 bug where rx.RestProp would silently drop forwarded kwargs that weren't declared fields of the target component — e.g. font_weight= — instead of routing them into emotion css the way a regular component does.

  • Core fix in memo.py: A _rest_target_fields: set[str] accumulator is added to MemoComponentDefinition and populated as a side-effect of body evaluation via _lift_rest_props. _MemoCallBinding.take_rest is updated to accept this set and split forwarded kwargs into plain props (declared target fields) vs. a single emotion-formatted css prop (everything else).
  • Two call paths updated: Both the eager (_create_component_definition) and lazy (memo()) definition paths correctly share the same mutable set so it is populated before the first call-site uses it.
  • Tests: Two new unit tests cover the CSS-routing and the "real field stays a prop" guard; the integration test gains a font-weight assertion; the take_rest unit test is updated to match the new two-argument API.

Confidence Score: 5/5

Safe to merge — the change is well-scoped, both code paths are covered, and the new tests guard the fixed and the preserved behaviors.

The shared-mutable-set pattern is correct for both the eager and lazy body-evaluation paths. take_rest's new two-way split is straightforward and matches how Component._post_init handles unknown kwargs. All touched logic has corresponding unit and integration tests. No regressions found in the common use cases.

No files require special attention.

Important Files Changed

Filename Overview
packages/reflex-base/src/reflex_base/components/memo.py Core fix: adds _rest_target_fields accumulator to MemoComponentDefinition and rewrites take_rest to route non-target-field kwargs into emotion css instead of as raw camelCase props. Logic is sound — shared mutable set is correctly populated via body evaluation before first call-site use.
tests/units/components/test_memo.py Adds two focused regression tests for the new CSS-prop routing behaviour and one test guarding that declared target fields stay as plain props; also updates the take_rest unit test to match the new two-argument signature.
tests/integration/test_memo.py Adds font_weight="bold" to the existing summary-card fixture and asserts the computed style is 700, providing an end-to-end integration guard for the fix.
docs/library/other/memo.md Adds a clear "Limitation: props consumed at build time" section with a working code example; documentation is accurate and the example compiles correctly.
packages/reflex-base/news/6605.bugfix.md One-line changelog entry accurately describing the fix.
pyi_hashes.json Regenerated .pyi content hashes; mechanical change reflecting updated type stubs.

Reviews (4): Last reviewed commit: "Merge branch 'main' into style-fix" | 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.

FarhanAliRaza and others added 2 commits June 15, 2026 22:50
Explain that rx.RestProp only forwards runtime props and cannot carry
props a target component consumes at build time (is_external, icon tag,
custom create()), and show declaring an explicit Var parameter as the
workaround.
@FarhanAliRaza FarhanAliRaza requested a review from Alek99 as a code owner June 15, 2026 19:41
@FarhanAliRaza

Copy link
Copy Markdown
Contributor Author

I added a documentation note.

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