Skip to documentation
Heimdall Docs
Heimdall Docs

The Heimdall Framework

Heimdall is an HTML-first, server-driven UI framework for ASP.NET Core. It keeps the browser simple, the server in control, and removes the need for a separate client-side application layer in many cases.

This page explains what Heimdall is, what problems it solves, and then gets you from zero to your first working page and interaction.

1. Why Heimdall Exists

Modern web apps often split UI across multiple layers: APIs, client state, and rendering frameworks. That split introduces complexity, duplication, and synchronization problems. Heimdall reduces that surface area by letting the server remain the source of truth and using HTML as the interaction boundary.

Text
Traditional stack:
UI -> JSON API -> client state -> DOM updates

Heimdall:
UI -> HTML -> DOM updates

2. What Heimdall Solves

Removes the need for a separate JSON API layer for most UI interactions.
Eliminates client-side state synchronization problems.
Keeps UI rendering and business logic together on the server.
Supports modern UX patterns (partial updates, live updates, streaming) without a SPA.

3. What Heimdall Is Not

Not a SPA framework.
Not a client-side state manager.
Not a replacement for APIs in all scenarios.
Not a virtual DOM or component runtime.

4. The Mental Model

Heimdall is built around a simple rule: the server owns the UI, and the browser applies updates.

C#
Pages -> return HTML

Invocations -> return HTML

Browser -> swaps HTML into the DOM

No client-side state required

5. Install

Install the Heimdall template package once, then choose the focused template that matches the app's ownership model. Visual Studio lists separate Native, MVC, Razor Pages, Blazor, and Static Site Generator entries; hosted templates expose only the authentication choices they support, while the static generator exposes artifact or web-root output. The Blazor template is static SSR and does not enable Interactive Server. If you only need server-side HTML composition, HeimdallFramework.Rendering can be installed without the full Server pipeline.

Shell
dotnet new install HeimdallFramework.Templates

# Native Heimdall application:
dotnet new heimdall-native -n MyHeimdallApp

# Other hosted ownership models:
dotnet new heimdall-mvc -n MyMvcApp
dotnet new heimdall-razor-pages -n MyRazorPagesApp
dotnet new heimdall-blazor -n MyBlazorApp

# Optional OpenID Connect scaffold for hosted apps:
dotnet new heimdall-native -n MyOidcApp --auth oidc

# Static-first application:
dotnet new heimdall-ssg -n MyStaticSite --output-mode artifact
dotnet new heimdall-ssg -n MyHostedStaticSite --output-mode web-root

# Standalone server-side HTML composition:
dotnet add package HeimdallFramework.Rendering

6. Optional Server Setup

The generated hosted app does not require rate limiting, but when an application needs a native ASP.NET Core policy for content invocations, configure it beside AddHeimdall. Apply the policy with [EnableRateLimiting] on the invocation or invocation class; use [DisableRateLimiting] for a narrow opt-out. If rate limiting is not needed, omit the limiter registration and middleware.

C#
using Microsoft.AspNetCore.RateLimiting;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddHeimdall();

builder.Services.AddRateLimiter(options =>
{
    options.RejectionStatusCode = StatusCodes.Status429TooManyRequests;
    options.AddFixedWindowLimiter("content-invocations", limiter =>
    {
        limiter.PermitLimit = 60;
        limiter.Window = TimeSpan.FromMinutes(1);
        limiter.QueueLimit = 0;
    });
});

var app = builder.Build();
app.UseRateLimiter();
app.UseHeimdall();

[EnableRateLimiting("content-invocations")]
[ContentInvocation("profile.save")]
public static IHtmlContent Save(ProfilePayload payload)
    => Profile.Render(payload);

7. Your First Page

A Heimdall page is just a route that returns HTML. No Razor is required.

C#
app.MapHeimdallPage("/", ctx =>
    FluentHtml.Div(div => div
        .Text("Hello from Heimdall")));

8. Your First Interaction

A Heimdall interaction sends a request to a server invocation, gets HTML back, and swaps that HTML into the DOM.

Strongly Typed Markup

C#
return FluentHtml.Button(button => button
    .Text("Click Me")
    .Heimdall(heimdall => heimdall
        .Click("counter.increment")
        .Target("#result")
        .SwapInner()));

Rendered HTML

HTML
<button
  heimdall-content-click="counter.increment"
  heimdall-content-target="#result"
  heimdall-content-swap="inner">
  Click Me
</button>

<div id="result"></div>

Invocation

Code
[ContentInvocationPrefix("counter")]
public sealed class CounterInvocations
{
    [ContentInvocation("increment")]
    public IHtmlContent Increment()
    {
        return FluentHtml.Div(div => div
            .Text("Updated from the server."));
    }
}

9. What Just Happened

1. The button click triggered a Heimdall request.
2. The server invocation returned fresh HTML.
3. Heimdall swapped that HTML into the target element.

Reusable Libraries and Frontend Plugins

Package server-rendered content and browser integrations through explicit public extension points.