Skip to content

Fix: Correct Var Rendering in JSX Templates and Restore Multi-Argument EventHandler Support#5978

Closed
ParagGhatage wants to merge 3 commits intoreflex-dev:mainfrom
ParagGhatage:fix/vars-render-event-args
Closed

Fix: Correct Var Rendering in JSX Templates and Restore Multi-Argument EventHandler Support#5978
ParagGhatage wants to merge 3 commits intoreflex-dev:mainfrom
ParagGhatage:fix/vars-render-event-args

Conversation

@ParagGhatage
Copy link
Copy Markdown

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)

Changes To Core Features:

  • Have you added an explanation of what your changes do and why you'd like us to include them?
  • Have you written new tests for your core changes, as applicable?
  • Have you successfully ran tests with your changes locally?

After these steps, you're ready to open a pull request.

Description:

This PR fixes a core rendering issue in Reflex’s JSX compiler, where Var objects passed through custom EventHandler signatures were interpreted as component mappings. This caused incorrect argument validation, internal VarTypeError, and malformed JavaScript generation.

The fix introduces proper handling for Var instances inside _RenderUtils.render(), ensuring they are treated as atomic JavaScript expressions, not component-like mappings.


Background

Users creating custom EventHandler signatures may return multiple Var expressions:

def submit_signature(event):
    return (
        Var(f"{event}?.formData"),
        Var(f"{event}?.retrievedSchema.title"),
    )

This is valid and expected. However, Reflex previously failed to process this scenario.


Symptoms of the Bug

1. Incorrect argument validation

Reflex raises:

Event on_submit only provides 1 argument

despite args_spec producing two separate Var objects.


2. Template rendering crashes with VarTypeError

The renderer runs:

if "iterable" in component:

When component is a Var, this triggers __contains__, causing:

VarTypeError: 'in' operator not supported for Var types

This stops compilation before codegen.


3. Malformed JavaScript output

Generated code incorrectly wraps Var expressions into arrow functions:

((_event?.formData) => ...)

instead of:

handler((_event?.formData), (_event?.retrievedSchema.title))

This makes the JS invalid and breaks all user-defined multi-argument event signatures.


🔍 Root Cause

A. _RenderUtils.render() assumes every non-string component is a dict-like structure

The original implementation:

if "iterable" in component:
    ...

This treats every input as a Mapping.
Var does not support "in" and raises errors when introspected.


B. Validation layer relies on post-rendered structure

Because rendering fails early, only one argument survives validation.
This causes:

EventFnArgMismatchError

even though the signature is correct.


C. JS codegen inherits the corrupted render tree

The malformed intermediate structure results in bad JS.


The Fix

1. Add Var support in _RenderUtils.render()

The function now short-circuits when receiving a Var:

from reflex.vars.base import Var

if isinstance(component, Var):
    return str(component)

This ensures:

  • Vars render as raw JS values
  • Template logic never introspects Vars
  • No "iterable" in component checks run
  • Vars behave consistently throughout the pipeline

2. Updated type signature to reflect dynamic behavior

Old:

def render(component: Mapping[str, Any] | str) -> str:

New:

def render(component: Mapping[str, Any] | str | Var | Any) -> str:

or Any, depending on linting preferences.

This resolves Pyright errors and aligns with the actual usage of the renderer.


Final Patched render() Implementation

class _RenderUtils:
    @staticmethod
    def render(component: Mapping[str, Any] | str | Var | Any) -> str:

        # If component is a Var, render it as raw JS and stop.
        from reflex.vars.base import Var

        if isinstance(component, Var):
            return str(component)

        if isinstance(component, str):
            return component or "null"

        if "iterable" in component:
            return _RenderUtils.render_iterable_tag(component)
        if "match_cases" in component:
            return _RenderUtils.render_match_tag(component)
        if "cond_state" in component:
            return _RenderUtils.render_condition_tag(component)
        if (contents := component.get("contents")) is not None:
            return contents or "null"
        return _RenderUtils.render_tag(component)

This change is minimal, safe, and fully backward-compatible.


Added Tests

test_var_render.py

Ensures:

assert _RenderUtils.render(Var("_event?.formData")) == "_event?.formData"

Result

After this fix:

  • Multi-argument event handlers work reliably
  • No more VarTypeError
  • Validation correctly counts arguments
  • Correct JS is generated
  • Fully compatible with all existing Reflex code

This PR resolves the underlying structural issue and removes the need for any workaround or regex hack.

Closes #5741

…ntHandler support

- Added early return for Var to avoid template logic ('in component')
- Updated render() type signature to accept Any (fixes Pyright mismatch)
- Prevents VarTypeError during event arg rendering
- Ensures args_spec with multiple Vars remains intact through validation
- Adds tests for Var rendering and event signature parsing
- Updates pyi_hashes.json due to signature changes
@ParagGhatage
Copy link
Copy Markdown
Author

@adhami3310 ,
This PR fixes the underlying issue we discussed — the one where custom
EventHandler signatures returning multiple Var expressions caused
incorrect argument validation, VarTypeErrors inside _RenderUtils.render(),
and malformed JavaScript output.

This avoids any regex-based workaround and addresses the core structural bug
cleanly. Let me know if you'd like additional tests or changes!

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Nov 14, 2025

Greptile Overview

Greptile Summary

This PR fixes a critical bug where Var objects in multi-argument EventHandler signatures caused VarTypeError and generated invalid JavaScript. The fix adds proper Var handling in _RenderUtils.render() by checking if the component is a Var instance and rendering it as a raw JS expression before attempting dict-like introspection.

Key Changes:

  • Added isinstance(component, Var) check at the start of _RenderUtils.render() to short-circuit and return str(component)
  • Updated type signature from Mapping[str, Any] | str to Mapping[str, Any] | str | Var | Any
  • Added test case test_var_render_raw_js() to verify Var objects render correctly

Issues Found:

  • Duplicate string check on lines 63-66 (lines 65-66 are exact copy of 63-64)
  • Redundant from reflex.vars.base import Var import inside the method (already imported at module level on line 13)

The core fix is sound and addresses the root cause described in issue #5741. Once the duplicate code and redundant import are cleaned up, this should safely resolve the reported JavaScript generation errors.

Confidence Score: 3/5

  • Safe to merge after fixing duplicate code and redundant import
  • The core logic fix is correct and addresses a real bug, but the PR contains two syntax issues (duplicate string check and redundant import) that should be cleaned up before merging. The test coverage is minimal but adequate for the specific fix.
  • reflex/compiler/templates.py requires attention to remove duplicate code on lines 65-66 and redundant import on line 58

Important Files Changed

File Analysis

Filename Score Overview
reflex/compiler/templates.py 3/5 Adds Var handling to _RenderUtils.render() to fix multi-argument EventHandler bug, but contains duplicate string check and redundant import
tests/units/vars/test_var_render.py 5/5 New test file with single test case verifying Var renders as raw JS string - covers basic case but could be more comprehensive

Sequence Diagram

sequenceDiagram
    participant User as EventHandler Definition
    participant Compiler as Component Renderer
    participant RenderUtils as _RenderUtils.render()
    participant JSGen as JS Code Generator

    User->>Compiler: on_submit with args_spec returning (Var1, Var2)
    Compiler->>RenderUtils: render(component_dict_with_Vars)
    
    alt component is Var (NEW)
        RenderUtils->>RenderUtils: isinstance(component, Var)?
        RenderUtils->>JSGen: return str(Var) as raw JS
    else component is string
        RenderUtils->>JSGen: return string or "null"
    else component is dict
        RenderUtils->>RenderUtils: Check "iterable", "match_cases", etc.
        RenderUtils->>RenderUtils: Recursively render children
        RenderUtils->>JSGen: return jsx() call
    end
    
    JSGen->>User: Valid JavaScript with multiple arguments
Loading

Copy link
Copy Markdown
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

3 files reviewed, 2 comments

Edit Code Review Agent Settings | Greptile

Comment on lines 56 to +66
if isinstance(component, str):
return component or "null"
if isinstance(component, str):
return component or "null"
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.

syntax: duplicate string check - lines 63-64 are identical to lines 65-66

Suggested change
if isinstance(component, str):
return component or "null"
if isinstance(component, str):
return component or "null"
if isinstance(component, str):
return component or "null"
Prompt To Fix With AI
This is a comment left during a code review.
Path: reflex/compiler/templates.py
Line: 63:66

Comment:
**syntax:** duplicate string check - lines 63-64 are identical to lines 65-66

```suggestion
        if isinstance(component, str):
            return component or "null"
```

How can I resolve this? If you propose a fix, please make it concise.

Comment thread reflex/compiler/templates.py Outdated
Comment on lines +57 to +58
# If component is a Var, render it as raw JS and stop.
from reflex.vars.base import Var
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.

style: redundant import - Var is already imported at the top of the file (line 13)

Suggested change
# If component is a Var, render it as raw JS and stop.
from reflex.vars.base import Var
# If component is a Var, render it as raw JS and stop.
Prompt To Fix With AI
This is a comment left during a code review.
Path: reflex/compiler/templates.py
Line: 57:58

Comment:
**style:** redundant import - `Var` is already imported at the top of the file (line 13)

```suggestion
        # If component is a Var, render it as raw JS and stop.
```

How can I resolve this? If you propose a fix, please make it concise.

@codspeed-hq
Copy link
Copy Markdown

codspeed-hq bot commented Nov 14, 2025

CodSpeed Performance Report

Merging #5978 will not alter performance

Comparing ParagGhatage:fix/vars-render-event-args (396229a) with main (a679316)

Summary

✅ 8 untouched

@masenf
Copy link
Copy Markdown
Collaborator

masenf commented Nov 14, 2025

This doesn't address the underlying issue in any way.

@masenf masenf closed this Nov 14, 2025
@ParagGhatage ParagGhatage deleted the fix/vars-render-event-args branch November 16, 2025 05:28
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.

Invalid JavaScript Code Generation with EventHandler args_spec Using rx.Var Expressions

2 participants