-
Notifications
You must be signed in to change notification settings - Fork 1.7k
on_submit should support TypedDict for form data #6264
Description
Describe the Enhancement you want
Currently, on_submit handlers take a form_data argument typed as dict[str, Any]. Users should be able to specify a TypedDict for better IDE integration and doc/schema generation for machine consumption.
With this information it should also be possible to determine at compile time if the form contains the fields necessary to satisfy the type and raise a helpful error: Form is missing field "fname" expected by handler MyState.on_submit
Which feature do you want to improve? (and what problem does it have)
Form.on_submit is loosely typed
What is the benefit of the enhancement?
- Better type checking
- Improved IDE hinting
- Better context for schema generation
Show an example/usecase were the improvement are needed.
Before (untyped):
import reflex as rx
class SignupState(rx.State):
def on_submit(self, form_data: dict[str, Any]):
# No IDE hints, no idea what keys exist
name = form_data["name"] # typo? missing field? no way to know
email = form_data["email"]
pasword = form_data["pasword"] # silent bug — misspelled key
def signup_form():
return rx.form(
rx.input(name="name", placeholder="Name"),
rx.input(name="email", placeholder="Email"),
rx.input(name="password", type="password", placeholder="Password"),
on_submit=SignupState.on_submit,
)After (typed with TypedDict):
import reflex as rx
from typing import TypedDict
class SignupData(TypedDict):
name: str
email: str
password: str
class SignupState(rx.State):
def on_submit(self, form_data: SignupData):
# IDE autocompletes form_data.name, form_data.email, form_data.password
name = form_data["name"]
email = form_data["email"]
password = form_data["password"]
# form_data["pasword"] # IDE/mypy flags this immediately
def signup_form():
return rx.form(
rx.input(name="name", placeholder="Name"),
rx.input(name="email", placeholder="Email"),
rx.input(name="password", type="password", placeholder="Password"),
on_submit=SignupState.on_submit,
)Compile-time validation — missing field example:
class SignupData(TypedDict):
fname: str
lname: str
email: str
class SignupState(rx.State):
def on_submit(self, form_data: SignupData):
...
def signup_form():
return rx.el.form(
rx.el.input(name="email", placeholder="Email"),
# "fname" and "lname" inputs are missing!
on_submit=SignupState.on_submit,
)Expected compile-time error output:
$ reflex run
──── Compile Error ─────────────────────────────────────────────────────────────
Form field mismatch in signup_form (app/pages/signup.py:14)
The on_submit handler `SignupState.on_submit` expects form data matching
`SignupData` with fields: fname, lname, email
Fields missing from form inputs:
✗ "fname" — no <input name="fname"> found in form
✗ "lname" — no <input name="lname"> found in form
Fields present:
✓ "email"
Hint: Add the missing input elements to your form, or remove the fields
from `SignupData` if they are optional.
────────────────────────────────────────────────────────────────────────────────
The typed version gives you three wins at once: IDE autocomplete on the dict keys, mypy/pyright catching typos in handler code, and Reflex itself validating at compile time that the form's name attributes match what the handler expects. The dict[str, Any] signature would continue to work as-is for anyone who doesn't need the stricter checking.