C20.0M20.0 Helix lets you build dynamic, interactive screens in the mobile app without writing a single line of C#. It is built on Rock's Lava Applications: an endpoint's Code Template emits XAML instead of HTML, the app swaps that XAML into the live page, and the person never leaves the screen they were on. Helix needs Rock 20.0 or later on the server and version 20.0 or later of the app. If you have used HTMX on the web, this will feel immediately familiar. Helix is Rock's HTMX-based approach, and the mobile vocabulary is a deliberate port of it. If you have used the mobile Content block's Callbacks, you already know most of the idea: Helix is that same swap loop, generalized so any element can start a request and any named element on the page can receive the result. Read Writing Fragments before you write your first endpoint. Your first Helix screen Three pieces: an element to receive the content, an element to go get it, and an endpoint to serve it. On the page. A layout to receive the content, and a button that fetches it: <VerticalStackLayout Spacing="12"> <VerticalStackLayout Hx.Id="GroupList"> <Label Text="Nothing loaded yet." /> </VerticalStackLayout> <Button Text="Load My Groups" StyleClass="btn,btn-primary" Hx.Get="^/group-toolbox/my-groups" Hx.Target="GroupList" Hx.Swap="inner" /> </VerticalStackLayout> The endpoint. A GET endpoint with the slug my-groups, in an application with the slug group-toolbox. Its Code Template is ordinary Lava that happens to emit XAML instead of HTML (25 here being whichever group type you care about): {% assign memberships = CurrentPerson | Groups:'25' %} <VerticalStackLayout Spacing="8"> {% for membership in memberships %} <Label Text="{{ membership.Group.Name | Escape }}" /> {% else %} <Label Text="You are not in any groups yet." /> {% endfor %} </VerticalStackLayout> Key concepts All Lava runs on the server. The app receives the rendered output, so a fragment is a snapshot of the moment it was served. See Writing Fragments. One request, one target. A response has a single root element and lands in one place. There are no out-of-band swaps and no response content selection. Setting a verb wires the interaction for you. You do not need Command="{Binding ...}" plumbing. The moment you put Hx.Get on a Button, its Clicked is wired. Put it on anything else and it gets a tap gesture. Targeting is by id, not by selector. HTMX aims at the DOM with CSS selectors. Helix aims at an element you have given an Hx.Id. Ids work across the whole page, which is what makes it possible for one block to update an element inside a different block. Vocabulary These terms come up constantly in the rest of these docs. TermMeaning EndpointA Lava Application endpoint whose Code Template emits XAML instead of HTML. FragmentThe XAML an endpoint returns, never the page XAML that fired the request. Always server-finished, always a single root element. InitiatorThe element whose interaction fired the request. Usually the element carrying the verb. TargetThe element the fragment is placed into or beside. With no Hx.Target, the initiator. SwapHow the fragment is placed: replace the contents, replace the element, append, and so on. TriggerWhen the request fires. Defaults to the element's natural interaction. HostWhat an id is looked up within, and what a request's lifetime is tied to. The page, or a cover sheet. Routes Mobile uses the same ^ shorthand web does. FormResolves to ^/app-slug/endpoint-slug/api/v2/lava-app/1/app-slug/endpoint-slug ^/endpoint-slugthe same, using the application from the enclosing Lava Application Content block /api/...that server path, verbatim Any of them may carry a query string, and query string values stay in the URL for every verb. Request headers Every Helix request identifies itself as the app, with X-Helix-Client: RockMobile and the app version in X-Helix-Shell-Version. It also carries the signed-in person's credentials, so CurrentPerson resolves and your Execute security verbs are enforced exactly as they are for a browser. Attribute Reference lists every header. To serve both a browser and the app from one endpoint, branch on the ClientType merge field rather than reading the header yourself. It is Mobile for the app and Web for everything else: {% if ClientType == 'Mobile' %} <VerticalStackLayout> <Label Text="Hello from the app" /> </VerticalStackLayout> {% else %} <div>Hello from the browser</div> {% endif %} NoteClientType is a rendering hint, never an authorization input. It is derived from a header that any caller can send, so branch presentation on it and never gate data access on it. See Security. In this section Writing Fragments Requests and Targeting Swaps and Animations Inheritance Triggers Forms and Values Indicators and Errors Endpoint Responses Cover Sheets Data Lists and Paging Tips and Tricks Security Limitations Attribute Reference