EvaluatedApplications.Blazier 1.1.0

dotnet add package EvaluatedApplications.Blazier --version 1.1.0
                    
NuGet\Install-Package EvaluatedApplications.Blazier -Version 1.1.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="EvaluatedApplications.Blazier" Version="1.1.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="EvaluatedApplications.Blazier" Version="1.1.0" />
                    
Directory.Packages.props
<PackageReference Include="EvaluatedApplications.Blazier" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add EvaluatedApplications.Blazier --version 1.1.0
                    
#r "nuget: EvaluatedApplications.Blazier, 1.1.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package EvaluatedApplications.Blazier@1.1.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=EvaluatedApplications.Blazier&version=1.1.0
                    
Install as a Cake Addin
#tool nuget:?package=EvaluatedApplications.Blazier&version=1.1.0
                    
Install as a Cake Tool

EvaluatedApplications.Blazier

Lazy, scaffold-first Blazor WebAssembly for static hosts such as GitHub Pages. Visitors get real HTML in well under a second, and the .NET runtime downloads only when they use something that needs it. Long C# jobs run in a Web Worker behind an ordinary interface.

Features

  • Static shells at publish time. Mark a component [BlazierPage("tools/prism")] and publish: the build renders it with HtmlRenderer and writes tools/prism/index.html from your own index.html. A visitor who never interacts downloads only that page (6.8 KB in the sample, against 2.8 MB for the same app booted the usual way).
  • Per-page head tags. Title and Description on [BlazierPage] replace the template's <title> and meta description in each shell. Set the MSBuild property BlazierSiteUrl and every shell also gets a canonical link and Open Graph tags (og:type, og:url, og:title, og:description).
  • On-intent loader. blazier.js (classic script, no dependencies) boots Blazor on click, visibility, idle or immediately, keeps the static shell visible until the component has rendered, and fixes the readiness race (the first Blazor.rootComponents.add used to throw "Dynamic root components have not been enabled"): islands mount on a ready handshake from the runtime, with a retry fallback.
  • Start on one control. By default a Click page starts on any press, key, focus or touch inside it. Give it a ClickTarget selector (for example "[data-blazier-go]") and only a click on that control starts the runtime, so a phone visitor who scrolls or taps text never downloads it.
  • Islands. Several named components on one static page, each booting the one shared runtime on its own trigger; <Island> marks them in Razor; data-blazier-island works in plain HTML too.
  • Build-time shells for I/O pages. Inject IBuildTime and skip fetching when IsPrerender is true, or wrap the live part in <NotAtBuildTime> with a skeleton. A page that still tries I/O fails the publish with BLZ102 instead of shipping an error message as static HTML.
  • Lazy assemblies with a guard. LazyIsland("chart", "Charts.PrimeChart, Charts", "Charts") mounts a component whose assembly downloads only on demand. After every build, a guard walks the IL reachable from startup and fails with BLZ001 (naming the path) if startup code reaches a lazy-assembly type, which would otherwise kill the runtime at boot with a type-load error.
  • Typed worker bridge. Mark an interface [WorkerService]; a source generator writes the proxy and the worker-side dispatcher. Calls are awaitable, IProgress<T> streams progress, IAsyncEnumerable<T> streams results with backpressure, a CancellationToken cancels cooperatively, and a job that never yields is stopped by terminating and restarting the worker after a grace period. Both known .NET 10 worker traps are handled inside the package.
  • Same protocol in-process. BlazierWorker.StartInProcessAsync runs your services on a dedicated thread with the same messages, for unit tests and non-browser hosts.

Requires .NET 10 and Microsoft.AspNetCore.Components.WebAssembly 10.0.11 (keep it equal to the installed browser-wasm runtime pack). The MSBuild steps run a small tool on Microsoft.AspNetCore.App 10.

Quick start

dotnet add package EvaluatedApplications.Blazier

In the project file, turn on scaffolding (and list lazy assemblies, if any):

<PropertyGroup>
  <BlazierScaffold>true</BlazierScaffold>
  
  <BlazierSiteUrl>https://example.org</BlazierSiteUrl>
</PropertyGroup>
<ItemGroup>
  <BlazierLazyAssembly Include="Charts" />
</ItemGroup>

In wwwroot/index.html, load the loader instead of starting Blazor yourself (keep the import map line):

<div id="app" data-blazier-island="app" data-blazier-start="immediate">Loading...</div>
<script src="_content/EvaluatedApplications.Blazier/blazier.js" defer></script>

Program.cs:

using Blazier;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Rendering;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;

var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.UseBlazier(b => b
    .Island<Greeting>("greeting")
    .LazyIsland("chart", "Charts.PrimeChart, Charts", "Charts"));
await builder.Build().RunAsync();

// Usually a .razor file. Published as hello/index.html; the runtime boots when its button is pressed.
[BlazierPage("hello", Island = "greeting", Start = StartOn.Click, ClickTarget = "[data-blazier-go]",
    Title = "Hello", Description = "A page that greets you before any .NET code runs.")]
public sealed class Greeting : ComponentBase
{
    [Inject] private IBuildTime Build { get; set; } = default!;

    protected override void BuildRenderTree(RenderTreeBuilder builder)
    {
        builder.AddContent(0, Build.IsPrerender ? "Hello (static) " : "Hello (live) ");
        builder.OpenElement(1, "button");
        builder.AddAttribute(2, "type", "button");
        builder.AddAttribute(3, "data-blazier-go", true);
        builder.AddContent(4, "Start");
        builder.CloseElement();
    }
}

A worker service:

using Blazier.Workers;

[WorkerService]
public interface IPrimeJobs
{
    Task<int> CountAsync(int limit, IProgress<double> progress, CancellationToken cancellationToken);
}

public sealed class PrimeJobs : IPrimeJobs
{
    public async Task<int> CountAsync(int limit, IProgress<double> progress, CancellationToken cancellationToken)
    {
        var slice = new CooperativeSlice();   // yields every 12 ms so a cancel message can be read
        int count = 0;
        for (int n = 2; n <= limit; n++)
        {
            bool prime = true;
            for (int d = 2; d * d <= n; d++) if (n % d == 0) { prime = false; break; }
            if (prime) count++;
            if (slice.IsDue) { progress.Report((double)n / limit); await slice.YieldIfDueAsync(cancellationToken); }
        }
        return count;
    }
}

public static class PrimeDemo
{
    // Call from a component, for example a button handler.
    public static async Task<int> RunAsync(Action<double> onProgress, CancellationToken cancellationToken)
    {
        await using var worker = await BlazierWorker.StartAsync();
        var jobs = worker.Get<IPrimeJobs>();
        return await jobs.CountAsync(5_000_000, new Progress<double>(onProgress), cancellationToken);
    }
}

How it fits together

Piece Where
UseBlazier, BlazierBuilder.Island/App/LazyIsland registers islands with the ready handshake
[BlazierPage] (Title, Description, Start, ClickTarget), StartOn, <Island> what the publish step writes, and what boots when
IBuildTime, BuildTime, <NotAtBuildTime>, IBlazierPrerenderSetup build-time rendering
blazier.js (Blazier.start/mount/configure, blazier:* events) the loader
[WorkerService], BlazierWorker, WorkerOptions, CooperativeSlice the worker bridge
MSBuild: BlazierScaffold, BlazierSiteUrl, BlazierLazyAssembly, BlazierLazyGuard publish and build steps

Loader options go on the script tag (data-blazor-script, data-framework-base to serve _framework from another path) or through Blazier.configure({...}). Each island element carries data-blazier-state (idle, booting, mounting, live, failed) for styling. In plain HTML, a click target is the attribute data-blazier-click-target="<selector>" next to data-blazier-start="click".

Limits worth knowing

  • The first interaction boots the runtime but is not replayed: the visitor presses again once the island is live.
  • Navigating between two static pages is a full page load, so in-memory state (a loaded model, say) does not carry over. Mount the routed app (App<TApp>()) to keep one runtime and one state across tool-to-tool moves.
  • The lazy guard is conservative: it follows startup code, routable pages and component render code, not lambdas run later (DI factories, event handlers).
  • A worker runtime costs about 32 MiB of memory and about 0.2 s to start from cached files (desktop measurement); on phones, memory is the budget.
  • Worker arguments and results travel as JSON (WorkerJson.Options); for trimmed apps add a source-generated JsonSerializerContext from a [ModuleInitializer] so both the page and the worker see it.

License

Proprietary: this package ships the compiled library only; see LICENSE for the full terms. Installing and using unmodified copies for internal evaluation and non-commercial development or testing is licensed; commercial, production, or revenue-generating use requires a separate commercial licence from the owner. No feature described here is gated behind a tier or a licence key. Copyright (c) 2026 Dongyang Stephen Chen, trading as Evaluated Applications. All rights reserved.

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.1.0 72 10/9/2026
1.0.0 82 10/8/2026