Patterns
Heimdall’s primitives are intentionally small, but real applications are built by combining them. This page focuses on recurring interaction patterns that emerge when triggers, payloads, swaps, state, OOB updates, polling, and SSE work together.
The goal is not to memorize isolated features. It is to recognize the shapes they form together.
1. What a Pattern Means in Heimdall
A pattern is a repeated composition of Heimdall primitives that solves a common interaction problem. Heimdall does not hide these patterns behind large abstractions. It lets them remain visible in the HTML and server rendering flow.
Trigger
+ Payload
+ Action
+ Target
+ Swap
+ optional State / OOB / Polling / SSE
-> reusable interaction pattern2. Why Patterns Matter
Heimdall stays small by keeping the primitive pieces explicit. Patterns matter because most application UI is not a single click or a single form. Real interfaces are coordinated flows across boundaries, and the patterns help explain how to compose those flows clearly.
Heimdall primitives stay small
-> patterns combine them
-> application UI emerges3. Pattern: Stateful Counter
One of the simplest and most useful Heimdall patterns is a self-contained stateful fragment. The current state lives on the DOM boundary, the button triggers the action, and an outer swap replaces the fragment with its next rendered state.
counter.Heimdall().State(new CounterState
{
Count = 1
});
button.Heimdall()
.Click("Counter.Increment")
.PayloadFromClosestState()
.SwapOuter();This pattern is valuable because it demonstrates the larger Heimdall model in miniature: state is on the DOM boundary, the action receives a DTO, and the server returns the next UI truth.
4. Pattern: Traditional Form with OOB Feedback
A traditional form can submit with closest-form payload binding and SwapNone, while the server responds with invocation output that updates other DOM regions out-of-band. This is a clean fit when the form itself should remain in place but other UI should react.
form.Heimdall()
.OnSubmit("Contact.Submit")
.PayloadFromClosestForm()
.SwapNone();
Response:
- toast updated OOB
- validation summary updated OOB
- success banner updated OOB5. Pattern: Interactive Form
Some forms are better treated as live UI rather than as a single submit moment. In that pattern, input, change, or blur triggers update part of the form or a nearby panel while the form remains the payload boundary.
input.Heimdall()
.Blur("Profile.ValidateDisplayName")
.PayloadFromClosestForm()
.Target("#display-name-validation")
.SwapInner();
select.Heimdall()
.Change("Pricing.Recalculate")
.PayloadFromClosestForm()
.Target("#quote-summary")
.SwapInner();This pattern works well when the user should see the server’s interpretation of the form before the final submit.
6. Pattern: Main Swap Plus OOB Side Effects
A very common Heimdall pattern is one primary target update combined with one or more OOB updates in the same response. This lets one request update the main working region while also coordinating toasts, counts, badges, or summary panels.
Request:
- trigger action
- main target swaps normally
Same response:
- toast updated OOB
- cart badge updated OOB
- sidebar summary updated OOB7. Pattern: Lazy Continuation Boundary
Lazy loading in Heimdall is not a separate subsystem. It is a composition of visible trigger, keyed closest-state payload, and outer swap on a sentinel boundary. That boundary acts as a continuation point for the next request.
sentinel.Heimdall()
.Visible("Weather.LoadMore")
.PayloadFromClosestState("weather")
.Target("#weather-sentinel")
.SwapOuter();The server returns the next chunk plus a new sentinel if more data remains.
rows...
+ next sentinel with updated state8. Pattern: Deferred Panel on Load
Some regions should exist in the page shell immediately but load their content through the normal action pipeline after the DOM is ready. This pattern uses a load trigger and an ordinary swap into the panel boundary.
panel.Heimdall()
.Load("Dashboard.Summary")
.Target("#summary-panel")
.SwapInner();9. Pattern: Polling Summary Region
Polling is a natural fit for small server-owned regions that should refresh periodically without a fresh user event. The boundary uses load plus PollMs, and the runtime handles repeated invocation while the element remains active.
panel.Heimdall()
.Load("Dashboard.Refresh")
.PayloadFromClosestState("dashboard")
.PollMs(5000)
.Target("#dashboard-panel")
.SwapOuter();This pattern works well when periodic freshness is enough and server push would be unnecessary overhead.
10. Pattern: Polling Plus OOB Coordination
A polling region can refresh its own content while also pushing secondary updates to other parts of the page through OOB fragments in the same response. This is useful when one timed refresh should keep several summary surfaces aligned.
Polling region refreshes
-> main panel updates
-> status badge updated OOB
-> queue count updated OOB
-> timestamp updated OOB11. Pattern: Keyboard-Driven Search
Heimdall works well for keyboard-first interactions because keydown can be combined with key filtering, closest-form payload, and a normal target swap. This keeps keyboard-specific logic declarative instead of pushing it into handwritten client code.
input.Heimdall()
.KeyDown("Search.Accept")
.Key("Enter")
.PayloadFromClosestForm()
.Target("#search-results")
.SwapInner();This pattern is useful when a field should respond to a meaningful key rather than to every keystroke.
12. Pattern: Hover Preview
A hover preview is a small but expressive Heimdall pattern. A hover trigger, optional hover delay, and a target panel let the browser request preview content only when the user dwells over a boundary long enough to make that preview meaningful.
row.Heimdall()
.Hover("Products.Preview")
.PayloadFromClosestState("product")
.HoverDelayMs(200)
.Target("#product-preview")
.SwapInner();13. Pattern: Cross-Boundary Command
A trigger does not have to live inside the same form or state boundary that provides its payload. Explicit payload selectors and self-sourced dataset payloads let a small command boundary interact with a larger region without tight structural coupling.
<button
heimdall-content-click="Search.Run"
heimdall-payload-from="#search-form"
heimdall-content-target="#search-results"
heimdall-content-swap="inner">
Search
</button>This pattern is useful when layout concerns and interaction concerns do not line up as a neat nested tree.
14. Pattern: Server-Owned Dashboard
A dashboard is often where Heimdall patterns become most visible. Different regions may use load for deferred startup, polling for periodic refresh, OOB updates for coordination, and SSE for real-time pushes where timing matters more than interval refresh.
Dashboard composition may include:
- load-triggered panels
- polling summaries
- OOB badge updates
- SSE-pushed activity feed
- stateful drilldown fragments15. Pattern: OOB and SSE Together
OOB and SSE are related but different. A common application pattern is to use OOB for coordinated side effects inside a user-driven response and SSE for updates that arrive later, outside that response. Together they let one page support both immediate and asynchronous UI coordination.
User action:
-> main swap
-> toast updated OOB
-> badge updated OOB
Later:
-> server pushes new status over SSE
-> activity feed updatesThis keeps the timing model clear: OOB is in-response coordination, SSE is out-of-response coordination.
16. Pattern: Assets, Templates, and Interactive Shells
Patterns are not only about triggers and swaps. Many applications use FluentHtml for the interactive shell, StaticAssets for file-based fragments, and templates for generated content blocks. Those rendering inputs can all participate in a single page or interaction flow.
Interactive shell -> FluentHtml
Static fragment -> StaticAssets.Get(...)
Generated content -> template render
Behavior -> Heimdall runtime17. What These Patterns Have in Common
Although the examples vary, the recurring Heimdall shape stays stable: the DOM expresses the current interaction boundary, the browser runtime interprets behavior, and the server returns the next HTML truth.
DOM boundary
-> runtime interprets behavior
-> server action runs
-> next HTML returns
-> DOM becomes current truth again18. Choosing the Right Pattern
The right pattern depends on the shape of the interaction problem rather than on the availability of a feature. The most important question is where the current UI truth lives and how the next UI truth should arrive.
Ask:
- Is this user-driven or time-driven?
- Is the payload form-shaped or state-shaped?
- Is the response local, coordinated, or pushed later?
- Should the boundary be replaced, updated within, or left in place?19. Common Mental Model
A useful way to think about Heimdall patterns is that they are repeated compositions of a very small number of ideas: boundaries, triggers, payloads, swaps, and optional coordination mechanisms such as OOB, polling, and SSE.
Small primitives
-> predictable compositions
-> larger application patternsNext Steps
With the main interaction patterns in place, the last important step is understanding the security boundaries around Heimdall: request authenticity, trusted markup, and what the runtime intentionally sanitizes or does not sanitize.