Build.NET

.NET #

The Microsoft.WebUI NuGet package wraps the Rust renderer's C ABI with safe handles and managed result types. It targets .NET 8 and .NET 9 and restores matching native runtime packages. Load one Protocol for repeated renders, then dispose native-backed objects when their work is complete.

Installation #

Check package availability first. NuGet artifacts are staged by the release pipeline, but automatic NuGet.org publishing is not enabled pending an approved signing path. Obtain matching packages from an available release or approved feed; the command below requires that feed to be configured.

dotnet add package Microsoft.WebUI

Load the protocol once and reuse it:

using Microsoft.WebUI;

using var protocol = new Protocol(
    await File.ReadAllBytesAsync("dist/protocol.bin"));
using var handler = new WebUIHandler("webui");

string html = handler.Render(
    protocol,
    """{"title":"Home"}""",
    "index.html",
    "/");

Progressive streaming #

StreamResponse creates a single-driver session. Start, Resume, and Advance return a StreamingStep containing Bytes, Done, and an optional Boundary descriptor.

using var session = handler.StreamResponse(protocol, "index.html", "/");

Response.ContentType = "text/html; charset=utf-8";
StreamingStep step = session.Start(initialStateJson);

while (true)
{
    await Response.Body.WriteAsync(step.Bytes);
    await Response.Body.FlushAsync();
    if (step.Done) break;

    if (step.Boundary is BoundaryDescriptor boundary)
    {
        string state = await LoadBoundaryStateAsync(
            boundary.Owner,
            boundary.Name,
            boundary.Key);
        step = session.Resume(
            boundary.InstanceId,
            state,
            BoundaryMode.Final);
    }
    else
    {
        step = session.Advance();
    }
}
MemberResult
Start(stateJson)Shell bytes through the first descriptor or terminal
Resume(instanceId, stateJson, mode)Only the pending occurrence's bytes through its checkpoint
Advance()Following parent bytes through the next descriptor or terminal
Update(instanceId, patchJson)Projected state bytes for an updatable occurrence

A descriptor requires Resume; no descriptor with Done == false requires Advance; Done == true means complete. Flush the boundary-only Resume result before waiting for more data. Advance renders the following parent or tail bytes.

The descriptor contains:

  • InstanceId, unique within this response
  • DeclarationId, stable within the compiled protocol
  • Owner, the entry or component template that authored the declaration
  • Name, local to that owner
  • Key, a BoundaryKey with Type, StringValue, and NumberValue

Commit an occurrence as BoundaryMode.Updatable to send later state:

byte[] chunk = session.Update(
    searchInstanceId,
    """{"query":"webui"}""");
await Response.Body.WriteAsync(chunk);
await Response.Body.FlushAsync();

Updates apply projected state to existing roots. They do not insert markup or rerun hydration, and are valid between an occurrence's Resume and Advance. The step that reports Done already includes the response tail and terminal record.

Drive one session from one request flow at a time. Independent sessions may run concurrently against the same handler and protocol. WebUIException carries the native diagnostic for invalid state, ordering, keys, or rendering.

Native assets #

The managed package restores the matching Microsoft.WebUI.Runtime.<rid> package transitively. Use WEBUI_LIB_PATH only when testing a custom local native build.

See Streaming Boundaries and the C ABI for the shared contract.