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
There are two ways to show a spinner, and which one you use depends on whether your trigger can hold children.
Container triggers: the CSS class convention
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.
Buttons and other leaf triggers: Hx.Indicator
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 alert has no title, just OK and Cancel buttons. Cancelling aborts before any request state changes. Because it inherits, a container or a form can declare one confirmation that covers all of its triggers.
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'] %}
Two details:
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 three 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
Three placements, chosen in this order:
Errors render as a notification box with an "Error" header. Both the slot placement and the beside-the-initiator placement replace the previous error rather than stacking, and both clear on the next success.
What produces an error
Two limitations to design around
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.