Patterns & Best Practices
15.1 Form Validation
Section titled “15.1 Form Validation”{ "id": "submit_btn", "type": "button","context": { "_label": "Submit","_disabled": "{{ empty(${email}) || empty(${password}) }}" },"style": { "common": {"opacity": "{{ empty(${email}) || empty(${password}) ? 0.4 : 1.0 }}" } } }15.2 Secure API Calls
Section titled “15.2 Secure API Calls”Store API tokens in a secured repository. Use the interceptor pattern in your remote repository so tokens are injected automatically. Never embed tokens in state or context values.
{ "repository": { "secured": { "id": "repo_keys", "config": {} } } }
{ "repository": { "remote": { "id": "repo_api","config": { "endPoint": "https://api.example.com","interceptor": { "adapter": { "operations": [ { "kind": "setBearerToken", "repositoryId": "repo_keys", "key": "api_token" }] } } } } } }15.3 Parsing API Responses
Section titled “15.3 Parsing API Responses”When an API returns a JSON body as a raw string, use strtojson() to parse it before binding to a dynamic component:
"_data": "{{ @{api_action.body} |> strtojson() }}"15.4 Search-as-you-type
Section titled “15.4 Search-as-you-type”{ "id": "search_field", "type": "textfield","context": { "_label": "Search", "_text": "${search}", "_debounce": 0.3 },"events": { "onChange": {"observed": "search","actions": [{ "repo": {"id": "do_search","input": { "repositoryId": "repo_api", "operation": "read","params": { "body": { "q": "${search}" } } },"debounce": 0.3,"output": { "success": [{ "state": { "id": "set_results","input": { "results": "@{do_search}" } } }] }} }]} } }15.5 Conditional UI from State
Section titled “15.5 Conditional UI from State”"_source": "{{ ${isFavorited} ? 'sf://star.fill' : 'sf://star' }}""foreground": { "color": "{{ ${isError} ? 'red' : 'green' }}" }"background": { "color": "{{ #{orientation} == 'portrait' ? 'yellow' : ${bgColor} }}" }15.6 Realtime Messaging Pattern
Section titled “15.6 Realtime Messaging Pattern”"onAppear": [{ "listen": {"id": "listen_messages","input": {"dependencyId": "repo_firebase","state": "messages","params": {"collection": "chat_messages","notification": {"service": { "id": "play_sound","input": { "serviceId": "audio_service_id","params": { "sound": "newMessage", "haptic": "light" } } }}}}} }]15.7 ID Naming Conventions
Section titled “15.7 ID Naming Conventions”IDs must be globally unique across the entire unit. A consistent prefix strategy prevents collisions:
- Modules: mod_
— e.g. mod_shop, mod_profile - Components:
_ — e.g. shop_search_field, shop_results_list - Actions:
action — e.g. shop_action_load_results, shop_action_set_error - Dependencies: dep_
_ — e.g. dep_remote_api, dep_keychain, dep_template_card
15.8 Skeleton / Loading State
Section titled “15.8 Skeleton / Loading State”Every leaf component accepts "_isLoading" in its context. When the bound value resolves to true, the component renders a shimmer skeleton in place of its normal content. The skeleton matches the component’s own size and shape as defined by style.layout — no hardcoded dimensions are applied.
Supported leaf components
text, button, image, video, pdf, media, label, map, textfield, texteditor, toggle, slider, progress, picker, datepicker
Context keys
| Context key | Type | Component | Description |
|---|---|---|---|
| _isLoading / isLoading | bool / expr | All leaves | When true, renders a shimmer skeleton sized to the component’s style.layout frame. No hardcoded sizes. |
| _skeletonHeight | number | text only | Explicit skeleton bar height in points. Use different values for titles (e.g. 24), subtitles (14), and captions (10). |
| _skeletonRows | number | dynamic only | Number of prototype skeleton rows to show while loading. Default: 5. |
Skeleton rendering rules
- The skeleton fills whatever frame
style.layoutdefines. When no explicit frame is set, the skeleton uses the component’s intrinsic size. - For
text,_skeletonHeightoverrides the bar height so titles and captions can have visually distinct weights. - For
dynamic: when_isLoadingistrue, the component renders_skeletonRowscopies of theprototypewith"isLoading": trueinjected into each prototype’s context. Every primitive inside the prototype then renders its own shaped skeleton independently.
Example — mixed leaf skeletons
{ "id": "skeletons_module_id", "type": "module", "context": { "_tabTitle": "skeletons", "_tabImage": "sf://sparkles" }, "state": { "isLoading": true, "focused": "", "toggleValue": false, "sliderValue": 0.65, "pickerSelection": "MacBook Pro", "textValue": "", "editorValue": "", "eventDate": "2026-03-24T12:00:00Z", "items": [ { "name": "Pro Display XDR", "subtitle": "Retina 6K" }, { "name": "Mac Studio", "subtitle": "M2 Ultra" } ] }, "children": [ { "id": "text_title_id", "type": "text", "context": { "_text": "The quick brown fox", "_skeletonHeight": 24, "_isLoading": "${isLoading}" }, "style": { "common": { "font": "title2" } } }, { "id": "text_subtitle_id", "type": "text", "context": { "_text": "Jumps over the lazy dog", "_skeletonHeight": 14, "_isLoading": "${isLoading}" } }, { "id": "demo_image_id", "type": "image", "context": { "_source": "sf://photo", "_isLoading": "${isLoading}" }, "style": { "layout": { "width": 60, "height": 60 } } }, { "id": "demo_button_id", "type": "button", "context": { "_label": "Continue", "_isLoading": "${isLoading}" }, "style": { "layout": { "height": 48, "maxWidth": 9999 } } }, { "id": "demo_picker_id", "type": "picker", "context": { "_label": "Device", "_selection": "${pickerSelection}", "_isLoading": "${isLoading}" }, "style": { "picker": "menu" } }, { "id": "demo_dynamic_id", "type": "dynamic", "context": { "_isLoading": "${isLoading}", "_skeletonRows": 3, "_data": "${items}" }, "prototype": { "id": "proto_row_id", "type": "hstack", "context": { "_spacing": 12 }, "children": [ { "id": "proto_image_id", "type": "image", "context": { "_source": "sf://shippingbox.fill", "_isLoading": "${isLoading}" }, "style": { "layout": { "width": 44, "height": 44 } } }, { "id": "proto_title_id", "type": "text", "context": { "_text": "@{item.name}", "_skeletonHeight": 16, "_isLoading": "${isLoading}" } } ] } } ]}15.9 State-Toggle Pattern for Loading
Section titled “15.9 State-Toggle Pattern for Loading”Use the state action with the ! (logical NOT) expression to toggle a boolean state key in place. The example below toggles isLoading on each tap — a common pattern for previewing skeleton states during development or toggling between a loading placeholder and real content in production.
"events": { "onTap": [ { "state": { "id": "toggle_loading_action_id", "input": { "isLoading": "{{ !${isLoading} }}" } } } ]}In production the isLoading key is typically set to true by a state action dispatched alongside the repo call (parallel action), then reset to false by a state action declared in the repo’s output.success / output.failure chain.
15.12 Formatting Numeric Display Values
Section titled “15.12 Formatting Numeric Display Values”Numeric values flow through the value pipeline as-is. A double like 0.7610442042350769 displays in _text with full IEEE 754 precision unless the author formats it. There is no implicit rounding — the principle in §11.1 (“authors own end-to-end value semantics — the runtime never rounds, casts, clamps, or relabels”) applies any time a numeric value is rendered to the user.
The three idiomatic ways to format a numeric for display:
1. _format descriptor — locale-aware number formatting
Section titled “1. _format descriptor — locale-aware number formatting”Apply via the _format context key on a text component (§4.3). _text MUST resolve to a numeric value when _format is present:
{ "id": "stat_avg", "type": "text", "context": { "_text": "${avgSeverity}", "_format": { "number": { "style": "decimal", "maximumFractionDigits": 1 } } } }Other _format shapes cover currency, percent, and duration — see §4.3.
2. cast(v, 'int') — discard fractional part
Section titled “2. cast(v, 'int') — discard fractional part”Useful for counts, integer scales, and any case where decimals aren’t meaningful:
"_text": "{{ 'Severity ' + cast(${severity}, 'int') + ' / 10' }}"3. Scale + cast — map a normalised value to an integer or percentage
Section titled “3. Scale + cast — map a normalised value to an integer or percentage”A slider produces a double in [0, 1]. To display as a 1-10 integer scale or as a percent:
"_text": "{{ cast(${value} * 9 + 1, 'int') + ' / 10' }}" // 1-10 integer"_text": "{{ cast(${value} * 100, 'int') + '%' }}" // 0-100 percentDefault discipline
Section titled “Default discipline”Treat raw ${doubleKey} or @{item.doubleField} in _text as a code smell. If you don’t actively want full IEEE 754 precision on screen, format the value. The same applies to numbers written into repo.create.body if they’ll be read back and displayed elsewhere — format at the consumer, not at the writer, so the stored value retains precision.