Developer Docs - Mobile Docs - Indicators and Errors

Everything on this page is about what the person sees while a request is in flight, and what they see when it fails.

Hx.Indicator

How you show a spinner depends on whether your trigger can hold children.

Container triggers

If your trigger is a layout, nest an indicator inside it with StyleClass="htmx-indicator". While a request is in flight the initiating element gets the htmx-request class, and the default stylesheet reveals any indicator inside it.


    

These rules ship in the default styles, matching web:

.htmx-indicator { opacity: 0; }
.htmx-request .htmx-indicator { opacity: 1; }

Opacity rather than IsVisible is intentional. It matches HTMX and avoids the layout jump you would get from collapsing the indicator's space.

Button triggers

A MAUI Button has no child content, only Text and ImageSource, and a sibling is not a descendant, so the CSS rule above cannot reach it. Point Hx.Indicator at the sibling's id instead.


    

Because a button is the most common trigger, this is the ordinary path, not an escape hatch. Reach for it first.

The app sets the named element's opacity to 1 and adds htmx-request to it, so a nested .htmx-indicator inside your indicator also reveals. The prior opacity is restored when the request finishes. If the id does not resolve, the request still fires; you just get no indicator.

Hx.Confirm

The message is the alert's title, with no body text, and OK and Cancel buttons. Cancelling aborts before any request state changes. Because it inherits, a container can declare one confirmation that covers all of its triggers. Be careful on a form: its fields' own triggers inherit it too, so a field with Hx.Trigger="Unfocused changed" asks for confirmation each time its own request fires. Hx.Prompt behaves the same way. To confirm only the submission, wrap the fields in a layout with Hx.Disinherit="Confirm Prompt". A submission's initiator is the form itself, so it keeps the confirmation.

Hx.Prompt

The value the person types is sent as the X-Helix-Prompt request header, not as a form value.

Read it in your endpoint from the Headers merge field:

{% assign note = Headers['X-Helix-Prompt'] %}

About what the person types:

Hx.DisabledElt

The initiating element is always disabled during flight, so you never need this for double-tap protection on the trigger itself. Use it for the other controls that should not be touched mid-request.

It takes a single id, not a list. The prior enabled state is captured and restored, so this never force-enables something that was already disabled.

Hx.Disable

Default false. It applies to the element and everything beneath it.


    

It does several jobs at once, which is worth knowing:

Hx.Notification

This is mobile-only, and it is the single most useful thing on this page. It declares an error slot by id: request errors render there instead of into your target or beside your button.



    
    

    

The slot has to be a ContentView or a Layout, because the error is placed with an inner swap. Pointing Hx.Notification at a Rock:NotificationBox does not work, since that is Border-derived. An empty <ContentView Hx.Id="..." /> is the idiomatic slot.

If the named slot cannot be found, the error still appears using the default placement below, so a failure is never invisible.

Where errors go

The app uses the first placement that applies, in this order:

Errors render as a notification box with an "Error" header. An error beside an initiator replaces only that initiator's previous error, and clears only when that same initiator next succeeds. A success from another trigger leaves it in place. A slot error clears on the next success from any trigger that uses the slot.

Error causes

Known limitations

An error rendered into the target uses an inner swap. So if your request appends rows to a list and it fails, the error replaces the whole list. Declare an Hx.Notification slot on anything that appends or prepends. This is the main practical reason to use one.

An error placed beside a trigger that sits directly in a Grid lands in the wrong cell. It gets no row or column of its own, so it overlaps whatever is in the first cell. The error is still visible, just badly positioned. Declaring an Hx.Notification slot avoids it entirely.