Browser History
Give a successful content action a canonical, shareable URL without turning Heimdall actions into REST navigation endpoints.
History is an explicit action response directive. It updates the address bar after response swaps complete; it is never rendered into the DOM.
1. Push or Replace
Push creates a new browser history entry. Replace updates the current entry. Neither performs an immediate page load.
return FluentHtml.Fragment(fragment => fragment
.Add(OrderPanel.Render(order))
.Heimdall(heimdall => heimdall
.HistoryPush("orders/42")));
// After a filter or tab correction that should not add Back history:
return FluentHtml.Fragment(fragment => fragment
.Add(OrderPanel.Render(order))
.Heimdall(heimdall => heimdall
.HistoryReplace("/orders/42?tab=activity")));2. Raw HTML Primitives
The fluent helpers emit ordinary directive elements. This is the exact response markup the browser runtime consumes and removes.
<history mode="push" url="orders/42"></history>
<history
mode="replace"
url="/orders/42?tab=activity">
</history>3. URL Normalization
A route with or without one leading slash is rooted at the current origin. It is never resolved relative to the current path directory.
orders/42 -> /orders/42
/orders/42 -> /orders/42
?tab=notes -> keeps the current path, replaces query/hash
#details -> keeps the current path and query
https://this-origin.example/orders/42 -> accepted
https://another-origin.example/... -> rejected
//another-origin.example/... -> rejected4. Back and Forward
A Heimdall history update changes only the URL immediately. When the user later traverses that managed entry with Back or Forward, Heimdall performs a normal full-page GET for the canonical URL. Every history URL therefore needs a real page route.
app.MapHeimdallPage("/orders/{id:int}", (route, ctx) =>
MainLayout.Render(
ctx,
OrderPage.Render(route.GetInt32("id")),
"Order"));
// Action response may now use:
return FluentHtml.Fragment(fragment => fragment
.Heimdall(heimdall => heimdall
.HistoryPush("orders/42")));5. Response Timing and Precedence
History is applied only after a successful content-action response has processed OOB directives, the main swap, and mutation reconciliation. A JavaScript-after directive sees the final URL. Redirect is a hard stop and suppresses history. Error responses strip history without applying it.
successful response:
1. OOB invocations and mutations
2. normal main-target swap (unless aborted)
3. mutation reconciliation
4. history push or replace
5. JavaScript "after" directives
<redirect> + <history> -> redirect wins
HTTP error + <history> -> history is stripped
<abort> + <history> -> main swap stops; explicit history still applies6. Lifecycle Events
The before event is cancellable and its mode or URL can be changed. After reports the normalized values. Error reports malformed, unsupported, duplicate, or cross-origin directives. Pop fires before Heimdall reloads a managed Back/Forward entry and can be cancelled when an application deliberately owns restoration.
document.addEventListener("heimdall:history-before", event => {
event.detail.url = addTenantPrefix(event.detail.url);
// event.preventDefault(); // suppress this update
});
document.addEventListener("heimdall:history-after", event => {
console.log(event.detail.mode, event.detail.url);
});
document.addEventListener("heimdall:history-error", event => {
console.error(event.detail.error);
});
document.addEventListener("heimdall:history-pop", event => {
// Cancel only if your own client router will restore the page.
});7. Contract and Guardrails
One successful action response may contain at most one history directive. Mode is push or replace. URLs must be same-origin, non-empty, and free of backslashes. History directives in Bifrost SSE messages are removed and ignored because background streams should not silently rewrite navigation.
HeimdallHtml.History(HeimdallHtml.HistoryMode.Push, "orders/42")
HeimdallHtml.HistoryPush("orders/42")
HeimdallHtml.HistoryReplace("/orders/42")
// There is intentionally no server-issued pop command.
// Back and Forward remain user/browser actions.