FormGroup
The FormGroup component wraps form inputs with labels, help text, and validation feedback in a single, clean component. It eliminates the boilerplate of manually creating Bootstrap form structures and handles validation states automatically.
Goal
By the end of this guide, you'll be able to create professional form fields with validation, help text, and required indicators in a single line of Python.
Quick Start
Here's the simplest way to create a form field.
Visual Examples & Use Cases
1. Validation States
Show users when their input is valid or invalid.
2. Required Fields
Automatically add required indicators.
3. Help Text
Guide users with contextual help.
Practical Functionality
Server-Side Validation
Integrate with backend validation.
@app.post("/register")
def register(req):
email = req.form.get("email")
password = req.form.get("password")
# Validate
errors = {}
if not email or "@" not in email:
errors["email"] = "Please enter a valid email"
if not password or len(password) < 8:
errors["password"] = "Password must be at least 8 characters"
if errors:
# Re-render form with errors
return Form(
FormGroup(
Input(name="email", value=email),
label="Email",
error=errors.get("email"),
is_invalid="email" in errors
),
FormGroup(
Input("password", input_type="password"),
label="Password",
error=errors.get("password"),
is_invalid="password" in errors
),
Button("Sign Up", type="submit")
)
# Success - create user
create_user(email, password)
return hx_redirect("/dashboard")
Form Error Summary
Render a compact error alert at the top of the form:
HTMX Live Validation
Validate as users type.
# Form with live validation
FormGroup(
Input(
name="username",
hx_post="/validate/username",
hx_trigger="keyup changed delay:500ms",
hx_target="next .feedback"
),
label="Username",
help_text="3-20 characters, letters and numbers only"
)
# Validation endpoint
@app.post("/validate/username")
def validate_username(username: str):
if len(username) < 3:
return Div(
"Username too short",
cls="invalid-feedback d-block feedback"
)
elif not username.isalnum():
return Div(
"Only letters and numbers allowed",
cls="invalid-feedback d-block feedback"
)
else:
return Div(
"Username available!",
cls="valid-feedback d-block feedback"
)
Complete Registration Form
def RegistrationForm():
return Form(
FormGroup(
Input(name="name"),
label="Full Name",
required=True
),
FormGroup(
Input("email", input_type="email"),
label="Email Address",
help_text="We'll never share your email",
required=True
),
FormGroup(
Input("password", input_type="password"),
label="Password",
help_text="At least 8 characters",
required=True
),
FormGroup(
Input("confirm_password", input_type="password"),
label="Confirm Password",
required=True
),
Button("Create Account", type="submit", variant="primary", full_width=True),
hx_post="/register",
hx_target="#form-container"
)
Integration Patterns
With Select and Textarea
FormGroup works with any form control.
# Select dropdown
FormGroup(
Select(
Option("Select country...", value="", selected=True),
Option("United States", value="us"),
Option("United Kingdom", value="uk"),
name="country"
),
label="Country",
required=True
)
# Textarea
FormGroup(
Textarea(name="bio", rows=4),
label="Bio",
help_text="Tell us about yourself (optional)"
)
With Custom Input Components
# With SearchableSelect
FormGroup(
SearchableSelect(
endpoint="/api/users/search",
name="assigned_to"
),
label="Assign To",
help_text="Search by name or email"
)
# With ThemeToggle
FormGroup(
ThemeToggle(current_theme="dark"),
label="Appearance"
)
Parameter Reference
| Parameter | Type | Default | Description |
|---|---|---|---|
input_element |
Any |
Required | Input, Select, or Textarea component |
label |
str \| None |
None |
Label text (optional) |
help_text |
str \| None |
None |
Help text shown below input |
error |
str \| None |
None |
Error message (shown when is_invalid=True) |
success |
str \| None |
None |
Success message (shown when is_valid=True) |
is_invalid |
bool |
False |
Whether to show invalid state |
is_valid |
bool |
False |
Whether to show valid state |
required |
bool |
False |
Whether field is required (adds asterisk) |
**kwargs |
Any |
- | Additional HTML attributes for container |
Best Practices
✅ Do This
# Use semantic validation
FormGroup(
Input("email", input_type="email"),
label="Email",
error="Please enter a valid email address",
is_invalid=True
)
# Provide helpful help text
FormGroup(
Input("password", input_type="password"),
label="Password",
help_text="At least 8 characters with 1 number",
required=True
)
# Show success feedback
FormGroup(
Input(name="username", value="john_doe"),
label="Username",
success="Username is available!",
is_valid=True
)
❌ Don't Do This
# Don't show both error and success
FormGroup(
Input(name="test"),
error="Error!",
success="Success!",
is_invalid=True,
is_valid=True # Confusing!
)
# Don't use vague error messages
FormGroup(
Input(name="email"),
error="Invalid", # Too vague
is_invalid=True
)
faststrap.components.forms.formgroup.FormGroup(input_element, label=None, help_text=None, error=None, success=None, is_invalid=False, is_valid=False, required=False, **kwargs)
Form group with label, input, help text, and validation feedback.
Wraps Bootstrap form controls with proper structure and validation states. Eliminates boilerplate for form field creation.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
input_element
|
Any
|
Input, Select, or Textarea component |
required |
label
|
str | None
|
Label text (optional) |
None
|
help_text
|
str | None
|
Help text shown below input |
None
|
error
|
str | None
|
Error message (shown when is_invalid=True) |
None
|
success
|
str | None
|
Success message (shown when is_valid=True) |
None
|
is_invalid
|
bool
|
Whether to show invalid state |
False
|
is_valid
|
bool
|
Whether to show valid state |
False
|
required
|
bool
|
Whether field is required (adds asterisk to label) |
False
|
**kwargs
|
Any
|
Additional HTML attributes for the container |
{}
|
Returns:
| Type | Description |
|---|---|
Div
|
Div containing label, input, and feedback |
Example
Basic form group:
FormGroup( ... Input(name="email", type="email"), ... label="Email Address", ... help_text="We'll never share your email" ... )
With validation error:
FormGroup( ... Input(name="password", type="password"), ... label="Password", ... error="Password must be at least 8 characters", ... is_invalid=True ... )
With success state:
FormGroup( ... Input(name="username", value="john_doe"), ... label="Username", ... success="Username is available!", ... is_valid=True ... )
Required field:
FormGroup( ... Input(name="name"), ... label="Full Name", ... required=True ... )
Note
The input_element should be a FastHTML Input, Select, or Textarea. Validation classes (is-invalid, is-valid) are automatically added to the input element based on is_invalid/is_valid flags.
Source code in src/faststrap/components/forms/formgroup.py
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 | |