Skip to content

Live Validation

LiveValidationField and ValidationMessage help you build HTMX-powered form validation without writing custom JavaScript.

Quick Start

from faststrap import Input, LiveValidationField

LiveValidationField(
    Input(name="email", placeholder="you@example.com"),
    validate_url="/validate/email",
    label="Email",
    help_text="We will validate this when the field changes.",
)

The input receives HTMX attributes and the server endpoint should return a replacement fragment compatible with the configured target and swap behavior.

Returning A Validation Message

from faststrap import ValidationMessage


@app.post("/validate/email")
def validate_email(email: str):
    if "@" not in email:
        return ValidationMessage("Enter a valid email address.", state="invalid")
    return ValidationMessage("Looks good.", state="valid")

Parameters

LiveValidationField

Parameter Type Default Description
input_element Any required Input component or FastHTML element to augment with HTMX attributes.
validate_url str required Endpoint called for validation.
label str \| None None Optional form label.
help_text str \| None None Helper text shown under the input.
error str \| None None Error text for invalid state.
success str \| None None Success text for valid state.
is_invalid bool False Marks the field invalid.
is_valid bool False Marks the field valid.
required bool False Marks the label/input as required.
method "get" \| "post" "post" HTTP method for validation request.
trigger str "blur changed delay:300ms" HTMX trigger string.
target str "closest .mb-3" HTMX target for the response.
swap str "outerHTML" HTMX swap strategy.
indicator str \| None None Optional HTMX loading indicator selector.
**kwargs Any Extra FormGroup attributes.

ValidationMessage

Parameter Type Default Description
message str \| None required Message to render. Returns None when empty.
state "invalid" \| "valid" \| "neutral" "invalid" Bootstrap feedback style.
**kwargs Any Extra HTML attributes.

Notes

  • LiveValidationField mutates the provided input element when it exposes an attrs mapping.
  • The default target replaces the nearest .mb-3, which matches Faststrap's FormGroup wrapper.
  • Return a full field wrapper when using swap="outerHTML", or return only a message when you target a feedback container.

API Reference

faststrap.components.forms.errors.LiveValidationField(input_element, validate_url, *, label=None, help_text=None, error=None, success=None, is_invalid=False, is_valid=False, required=False, method='post', trigger='blur changed delay:300ms', target='closest .mb-3', swap='outerHTML', indicator=None, **kwargs)

FormGroup wrapper that wires an input for HTMX live validation.

The validation endpoint should return a replacement FormGroup or another fragment compatible with the configured hx_target/hx_swap.

Source code in src/faststrap/components/forms/errors.py
@register(category="forms")
def LiveValidationField(
    input_element: Any,
    validate_url: str,
    *,
    label: str | None = None,
    help_text: str | None = None,
    error: str | None = None,
    success: str | None = None,
    is_invalid: bool = False,
    is_valid: bool = False,
    required: bool = False,
    method: Literal["get", "post"] = "post",
    trigger: str = "blur changed delay:300ms",
    target: str = "closest .mb-3",
    swap: str = "outerHTML",
    indicator: str | None = None,
    **kwargs: Any,
) -> Any:
    """FormGroup wrapper that wires an input for HTMX live validation.

    The validation endpoint should return a replacement `FormGroup` or another
    fragment compatible with the configured `hx_target`/`hx_swap`.
    """
    if hasattr(input_element, "attrs"):
        if method == "get":
            input_element.attrs["hx-get"] = validate_url
        else:
            input_element.attrs["hx-post"] = validate_url
        input_element.attrs["hx-trigger"] = trigger
        input_element.attrs["hx-target"] = target
        input_element.attrs["hx-swap"] = swap
        if indicator:
            input_element.attrs["hx-indicator"] = indicator

    return FormGroup(
        input_element,
        label=label,
        help_text=help_text,
        error=error,
        success=success,
        is_invalid=is_invalid,
        is_valid=is_valid,
        required=required,
        **kwargs,
    )

faststrap.components.forms.errors.ValidationMessage(message, *, state='invalid', **kwargs)

Render a Bootstrap-compatible validation feedback message.

This is useful for HTMX live validation endpoints that return only the small feedback fragment for one field.

Source code in src/faststrap/components/forms/errors.py
@register(category="forms")
def ValidationMessage(
    message: str | None,
    *,
    state: Literal["invalid", "valid", "neutral"] = "invalid",
    **kwargs: Any,
) -> Div | None:
    """Render a Bootstrap-compatible validation feedback message.

    This is useful for HTMX live validation endpoints that return only the
    small feedback fragment for one field.
    """
    if not message:
        return None

    cls = kwargs.pop("cls", "")
    state_cls = {
        "invalid": "invalid-feedback d-block",
        "valid": "valid-feedback d-block",
        "neutral": "form-text text-muted",
    }[state]
    attrs: dict[str, Any] = {"cls": f"{state_cls} {cls}".strip()}
    attrs.update(convert_attrs(kwargs))
    return Div(message, **attrs)