Custom Component Form Submission Event Handler Not Triggering in Reflex - Advanced Interactive Feedback Form Case

Problem Description:

I have created an advanced, interactive feedback form that:

  1. Uses custom React components for enhanced user experience
  2. Implements animated radio button inputs styled as emotive faces
  3. Provides dynamic, interactive feedback visualization
  4. Requires custom styling and animations beyond standard Reflex form capabilities

What’s Working:

  • Props data flow is functioning perfectly - demonstrated by props.message successfully passing data between Reflex and my custom React component (from reflex backend to the own react component)
  • Custom styling and animations are rendering as intended

Technicalities:

  1. A form with radio button inputs
  2. The form is defined in a custom JS file (hello.js) and uses props.on_submit for form submission
  3. On the Reflex side, I’ve created a custom component class (Hello) that:
  • Defines message and on_submit props
  • Successfully passes the message prop to React
  • However, the form submission event handler is not being triggered when the submit button is clicked
  • The WebSocket connection opens and closes, but the event handler doesn’t execute

** The core technical challenge is:**

  • While props.message works flawlessly showing data flow is possible, the props.on_submit event is not triggering the backend event handler
  • Need to properly connect this custom React form component with Reflex’s event handling system
    (* Maintain all the custom styling and interactivity that makes this feedback form competitive and engaging*) - this works up to now

The core issue is understanding how to properly connect a custom component’s form submission event in React with Reflex’s event handling system.

** The hello.js file:**

// assets/hello.js
import React from 'react';

export function Hello(props) {
  return (
    <>
      {/* Added a dedicated div for my message streaming from reflex backend */}
      <div className="message-container">
        <h1>{props.message}</h1>
      </div>
      
      <div className="background-layer"></div>
      
      <form onSubmit={props.on_submit}>
        <div className="feedback">
          <label className="angry">
            <input type="radio" defaultValue={1} name="feedback" />
            <div>
              {/* Additional content goes here */}
            </div>
          </label>
          
          {/* Adding other radio buttons here */}
          
          {/* Submit button */}
          <button type="submit">Submit</button>
        </div>
      </form>
    </>
  );
}

main component in my main reflex .py app:

class Hello(rx.Component):
    # Using an absolute path starting with /public
    library = "/public/hello"
    # Defining everything else as normal.
    tag = "Hello"
    #Props
    message: rx.Var[int] 
    on_submit: rx.EventHandler[lambda form_data: [form_data]] # Specifying to take one argument

**rx State **:

value:int = 0

class FormState(rx.State):
 @rx.event
    def handle_value(self, form_data: dict):
        """Handle the form submit"""
        print(f"the submitted value: {form_data["feedback"]}")
        self.value = form_data["feedback"]

** final render**:

    return rx.box(
    Hello.create(
        message=0,  #this would be place holder to show the FormState.value
        on_submit = FormState.handle_value

    ))

demo example:

Any help is appreciated :pray:

First issue is that the props get passed to your custom component in camelCase, so you should be referencing props.onSubmit in your JS code.

<form onSubmit={props.onSubmit}>

Second issue is that the form data from onSubmit in react is not directly serializable and cannot be sent to the backend without some additional processing.

The simplest way to deal with that is to inherit the functionality of Reflex’s built in form on_submit handling:

from reflex.components.el.elements.forms import Form


class Hello(Form):
    # Using an absolute path starting with /public
    library = "/public/hello"
    # Defining everything else as normal.
    tag = "Hello"
    # Props
    message: rx.Var[int]

Because your custom component now inherits from Form, you’ll match reflex’s rx.form on_submit handling, which includes things like preventDefault on the submit event, avoiding the page navigation/websocket breakage you were observing. The builtin on_submit handling also works with controls that set either name or id props.

2 Likes

Just tested, runs like clockwork! Many thanks for the guide @masenf

here is a demo for motivation :slight_smile:

feedbackAnimation_lr

The current rx.form() component supports the on_submit event handler and it works super! However, currently it does not include on_change or on_click handlers.

Use Case:
I am building an animated feedback form where the user selects an option using radio buttons. To simplify the user experience, I want to remove the need for an extra submit button. Instead, I need a way to trigger the onSubmit event automatically when the user clicks on any of the inputs within the form (e.g., a radio button).

Request:
Could rx.form() support an on_change or on_click handler for individual input elements? Alternatively, could it allow for form-level configuration to trigger on_submit immediately upon input selection?

I tried to solve this using my .js component but im running into issues:

  • Trying to manually trigger the form’s onSubmit event. However, this approach caused errors like TypeError: ev.preventDefault is not a function and TypeError: parameter 1 is not of type 'HTMLFormElement'. These issues I feel stem from attempting to trigger form submissions or manipulate the DOM in ways Reflex’s framework doesn’t seem to natively support.

It wouldn’t really make sense for the form to support on_change or on_click (actually it might support click, but probably doesn’t do what you want).

Instead you need to make your radio buttons trigger the submit when they’re clicked.

From your code sample above, this can be achieved in the JS code via

<input type="radio" defaultValue={1} name="feedback" onClick={(e) => e.target.form.requestSubmit()}/>