Soenneker.Blazor.WebWorkers
4.0.18
Prefix Reserved
dotnet add package Soenneker.Blazor.WebWorkers --version 4.0.18
NuGet\Install-Package Soenneker.Blazor.WebWorkers -Version 4.0.18
<PackageReference Include="Soenneker.Blazor.WebWorkers" Version="4.0.18" />
<PackageVersion Include="Soenneker.Blazor.WebWorkers" Version="4.0.18" />
<PackageReference Include="Soenneker.Blazor.WebWorkers" />
paket add Soenneker.Blazor.WebWorkers --version 4.0.18
#r "nuget: Soenneker.Blazor.WebWorkers, 4.0.18"
#:package Soenneker.Blazor.WebWorkers@4.0.18
#addin nuget:?package=Soenneker.Blazor.WebWorkers&version=4.0.18
#tool nuget:?package=Soenneker.Blazor.WebWorkers&version=4.0.18
Soenneker.Blazor.WebWorkers
Run background work in Blazor without freezing the UI.
This package gives you one simple API for browser web workers in Blazor.
Use it when you want to:
- move CPU-heavy work off the UI thread
- keep the app responsive while work is running
- run custom JavaScript worker jobs
- run exported C# methods in JS
- monitor progress, cancel requests, and inspect worker pool state
Install
dotnet add package Soenneker.Blazor.WebWorkers
The Basic Idea
Soenneker.Blazor.WebWorkers manages worker pools for you.
In most apps, the flow is:
- Register
IWebWorkersUtil - Call
Initialize() - Create a worker pool
- Queue work
- Optionally monitor progress, cancel work, or inspect snapshots
The same IWebWorkersUtil service works for both:
- JavaScript workers
.NETworkers
Quick Start
Register the service in Program.cs:
builder.Services.AddWebWorkersUtilAsScoped();
Inject it where you want to use it:
@inject IWebWorkersUtil WebWorkers
Initialize it once before first use:
await WebWorkers.Initialize();
Common Workflow
1. Create a JavaScript worker pool
Point the pool at your worker script:
await WebWorkers.CreatePool(new WebWorkerPoolOptions
{
WorkerCount = 4,
ScriptPath = "js/workers/app.worker.js"
});
That creates the default JavaScript pool.
2. Queue a JavaScript job
Pass a workload name and payload:
using System.Text.Json;
using Soenneker.Blazor.WebWorkers.Dtos;
WebWorkerResult<JsonElement> result = await WebWorkers.Run<JsonElement>(
"prime-analysis",
new
{
upperBound = 180000
},
progress =>
{
Console.WriteLine($"{progress.Percent:0}% - {progress.Message}");
return ValueTask.CompletedTask;
});
Your worker script is responsible for understanding the workload name and payload.
3. Cancel or inspect work
await WebWorkers.CancelRequest("default", jobId);
WebWorkerCoordinatorSnapshot snapshot = await WebWorkers.GetCoordinatorSnapshot();
JavaScript Worker Path
This is the best fit when your worker logic already lives in JavaScript, or when you want full control over the worker script.
Important points:
- JavaScript pools use
WebWorkerBackend.JavaScript - the default pool name is
"default" - you usually only need
WorkerCountandScriptPath - jobs are queued with a
workloadNameand optionalpayload - you can report progress back while work is running
You can also target a specific named pool:
await WebWorkers.CreatePool(new WebWorkerPoolOptions
{
Name = "images",
WorkerCount = 2,
ScriptPath = "js/workers/image.worker.js"
});
WebWorkerResult<JsonElement> result = await WebWorkers.Run<JsonElement>(
"images",
"generate-thumbnail",
new
{
width = 300,
height = 300
});
Worker scripts from a Razor class library
If a worker file ships from an RCL, build the static asset path like this:
string workerPath = WebWorkerAssetPaths.WorkerFromPackage(
"Soenneker.Blazor.Opfs",
"opfs.worker.js");
Then use that path as the pool's ScriptPath.
.NET Worker Path
This package can also run exported C# methods inside a browser worker by booting a second .NET WebAssembly runtime in that worker.
This path is useful when:
- your work is already written in C#
- you want to keep background logic in your Blazor app
- you do not want to hand-write a JavaScript worker for that job
Requirements
The .NET worker path requires:
- Blazor WebAssembly
AllowUnsafeBlocks=truein the app.csproj- exported worker methods defined in the main app assembly
- worker methods marked with
[JSExport]
Example .csproj setting:
<PropertyGroup>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
Define an exported worker method
using System.Runtime.InteropServices.JavaScript;
using System.Runtime.Versioning;
using Soenneker.Utils.Json;
[SupportedOSPlatform("browser")]
public static partial class WorkerExports
{
[JSExport]
public static string? AnalyzePrimeRange(int upperBound)
{
return JsonUtil.Serialize(new
{
upperBound,
message = "Ran inside a .NET worker."
});
}
}
Create a .NET worker pool
using Soenneker.Blazor.WebWorkers.Enums;
using Soenneker.Blazor.WebWorkers.Options;
await WebWorkers.CreatePool(new WebWorkerPoolOptions
{
Backend = WebWorkerBackend.DotNet,
WorkerCount = 1
});
Invoke the exported method
You can call it with a request object:
using Soenneker.Blazor.WebWorkers.Dtos;
using Soenneker.Blazor.WebWorkers.Enums;
WebWorkerResult<string?> result = await WebWorkers.Run<string?>(new WebWorkerRequest
{
Backend = WebWorkerBackend.DotNet,
MethodName = "MyApp.WorkerExports.AnalyzePrimeRange",
Arguments = [220000]
});
Or use the expression-based overload:
WebWorkerResult<MyResult> result =
await WebWorkers.Run(() => WorkerExports.RunAnalysisAsync(220000));
The expression-based overload is often the easiest option because it avoids building the request manually.
Important Types
IWebWorkersUtil
The main service you work with. It handles:
- initialization
- pool creation and destruction
- job execution
- cancellation
- pool and coordinator snapshots
WebWorkerPoolOptions
Used when creating a pool.
The most important properties are:
BackendNameScriptPathWorkerCountWorkerTypeRuntimeScriptPathBootConfigPathRestartFaultedWorkers
WebWorkerRequest
Used when you need full control over a queued request.
The most important properties are:
PoolNameBackendRequestIdWorkloadNameMethodNamePayloadArgumentsTimeoutMs
Monitoring and Cancellation
You can:
- receive progress callbacks while a job is running
- cancel a queued or running request
- inspect one pool or all pools
- inspect a full coordinator snapshot
Examples:
await WebWorkers.CancelRequest("default", requestId);
WebWorkerPoolSnapshot? pool = await WebWorkers.GetPoolSnapshot("default");
IReadOnlyList<WebWorkerPoolSnapshot> pools = await WebWorkers.GetPoolSnapshots();
WebWorkerCoordinatorSnapshot snapshot = await WebWorkers.GetCoordinatorSnapshot();
Things To Know
- JavaScript and
.NETworkers share the same top-level service:IWebWorkersUtil - JavaScript jobs use
WorkloadNameplusPayload .NETjobs useMethodNameplusArguments- the
.NETworker path is for Blazor WebAssembly .NETworker methods should usually return simple values or serialized JSON- if your worker code naturally returns
ValueTask, expose a smallTask-returning[JSExport]wrapper - cancellation of a running
.NETworker request may require terminating and replacing the backing worker
Which Path Should I Use?
Use the JavaScript path when:
- you already have worker logic in JavaScript
- you need a custom browser worker script
- your workload is naturally message-based
Use the .NET path when:
- your workload is already implemented in C#
- you want to stay in C# as much as possible
- you are building a Blazor WebAssembly app (Not available in Server)
| Product | Versions 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. |
-
net10.0
- Soenneker.Blazor.Utils.Ids (>= 4.0.4)
- Soenneker.Blazor.Utils.ModuleImport (>= 4.0.1796)
- Soenneker.Utils.Json (>= 4.0.2514)
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 |
|---|---|---|
| 4.0.18 | 0 | 4/15/2026 |
| 4.0.17 | 3 | 4/15/2026 |
| 4.0.16 | 39 | 4/15/2026 |
| 4.0.15 | 30 | 4/14/2026 |
| 4.0.14 | 33 | 4/14/2026 |
| 4.0.13 | 50 | 4/13/2026 |
| 4.0.12 | 47 | 4/13/2026 |
| 4.0.11 | 42 | 4/13/2026 |
| 4.0.10 | 74 | 4/8/2026 |
| 4.0.9 | 70 | 4/8/2026 |
| 4.0.8 | 87 | 4/8/2026 |
| 4.0.7 | 88 | 4/8/2026 |
| 4.0.6 | 82 | 4/8/2026 |
| 4.0.5 | 79 | 4/7/2026 |
| 4.0.4 | 84 | 4/6/2026 |
| 4.0.3 | 87 | 4/5/2026 |
| 4.0.2 | 94 | 4/3/2026 |
| 4.0.1 | 88 | 4/1/2026 |