Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
200 changes: 199 additions & 1 deletion aspnetcore/blazor/blazor-with-dotnet-on-web-workers.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
---
title: ASP.NET Core Blazor with .NET on Web Workers
ai-usage: ai-assisted
author: guardrex
description: Learn how to use Web Workers to enable JavaScript to run on separate threads that don't block the main UI thread for improved app performance in a Blazor WebAssembly app.
monikerRange: '>= aspnetcore-8.0'
ms.author: wpickett
ms.custom: mvc
ms.date: 11/20/2025
ms.date: 03/13/2026
uid: blazor/blazor-web-workers
---
# ASP.NET Core Blazor with .NET on Web Workers
Expand All @@ -18,6 +19,201 @@ uid: blazor/blazor-web-workers

Modern Blazor WebAssembly apps often handle CPU-intensive work alongside rich UI updates. Tasks such as image processing, document parsing, or data crunching can easily freeze the browser's main thread. Web Workers let you push that work to a background thread. Combined with the .NET WebAssembly runtime, you can keep writing C# while the UI stays responsive.

:::moniker range=">= aspnetcore-11.0"

The `webworker` project template provides built-in scaffolding for running .NET code in a Web Worker. The template generates the required JavaScript worker scripts and a C# `WebWorkerClient` class, which removes the need to write the interop layer manually. To learn about Web Workers with React, see <xref:client-side/dotnet-on-webworkers>.

> [!NOTE]
> The `webworker` template isn't limited to Blazor. The template works with any .NET WebAssembly host, including standalone `wasmbrowser` apps and custom JavaScript frontends, such as React or vanilla JS. In non-Blazor scenarios, import the template's JavaScript client (`dotnet-web-worker-client.js`) directly from your entry point and call `[JSExport]` methods without the Blazor-specific C# `WebWorkerClient` class.

## Create the projects

Create a Blazor WebAssembly app and a .NET Web Worker class library:

```dotnetcli
dotnet new blazorwasm -n SampleApp
dotnet new webworker -n WebWorker
```

Add a project reference from the app to the worker library:

```dotnetcli
cd SampleApp
dotnet add reference ../WebWorker/WebWorker.csproj
```

## Enable `AllowUnsafeBlocks`

Enable the <xref:Microsoft.Build.Tasks.Csc.AllowUnsafeBlocks> property in the app's project file (`SampleApp.csproj`), which is required for [`[JSExport]` attribute](xref:System.Runtime.InteropServices.JavaScript.JSExportAttribute) usage:

```xml
<PropertyGroup>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
```

> [!WARNING]
> The JS interop API requires enabling <xref:Microsoft.Build.Tasks.Csc.AllowUnsafeBlocks>. Be careful when implementing your own unsafe code in .NET apps, which can introduce security and stability risks. For more information, see [Unsafe code, pointer types, and function pointers](/dotnet/csharp/language-reference/unsafe-code).

## Define worker methods

Worker methods are `static` methods marked with [`[JSExport]`](xref:System.Runtime.InteropServices.JavaScript.JSExportAttribute) in a `static partial class`. Define them in the main application project because the assembly name must match the one used by the worker runtime.

Due to `[JSExport]` limitations, worker methods can only return primitives or strings. For complex types, serialize to JSON before returning. The `WebWorkerClient` automatically deserializes JSON results.

`Worker.cs`:

```csharp
using System.Runtime.InteropServices.JavaScript;
using System.Runtime.Versioning;
using System.Text.Json;

[SupportedOSPlatform("browser")]
public static partial class Worker
{
[JSExport]
public static string Greet(string name) => $"Hello, {name}!";

[JSExport]
public static string GetUsers()
{
var users = new List<User> { new("Alice", 30), new("Bob", 25) };
return JsonSerializer.Serialize(users);
}
}

public record User(string Name, int Age);
```

## Use the worker from a component

Inject `IJSRuntime` and use `WebWorkerClient.CreateAsync` to create a worker instance. The client manages the JavaScript messaging layer on your behalf.

`Pages/Home.razor`:

```razor
@page "/"
@using WebWorker
@implements IAsyncDisposable
@inject IJSRuntime JSRuntime

<PageTitle>Home</PageTitle>

<h1>Web Worker demo</h1>

<button class="btn btn-primary" @onclick="CallWorker" disabled="@(worker is null)">
Call Worker
</button>

@if (!string.IsNullOrEmpty(greeting))
{
<p>@greeting</p>
}

@if (users is not null)
{
<ul>
@foreach (var user in users)
{
<li>@user.Name (age @user.Age)</li>
}
</ul>
}
```

`Pages/Home.razor.cs`:

```csharp
using System.Runtime.Versioning;
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
using WebWorker;

namespace SampleApp.Pages;

[SupportedOSPlatform("browser")]
public partial class Home : ComponentBase, IAsyncDisposable
{
private WebWorkerClient? worker;
private string greeting = string.Empty;
private List<User>? users;

protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
worker = await WebWorkerClient.CreateAsync(JSRuntime);
StateHasChanged();
}
}

private async Task CallWorker()
{
if (worker is null)
{
return;
}

greeting = await worker.InvokeAsync<string>(
"SampleApp.Worker.Greet", ["World"]);

users = await worker.InvokeAsync<List<User>>(
"SampleApp.Worker.GetUsers", []);
}

public async ValueTask DisposeAsync()
{
if (worker is not null)
{
await worker.DisposeAsync();
}
}
}
```

## Template output

The `dotnet new webworker` template generates a class library with the following structure:

```
WebWorker/
├── WebWorker.csproj
├── WebWorkerClient.cs
└── wwwroot/
├── dotnet-web-worker-client.js
└── dotnet-web-worker.js
```

* `WebWorkerClient.cs`: C# client that manages worker lifecycle and communication.
* `dotnet-web-worker-client.js`: JavaScript class that creates the worker, dispatches messages, and resolves pending requests.
* `dotnet-web-worker.js`: Worker entry point that boots the .NET WebAssembly runtime and dynamically resolves `[JSExport]` methods by name.

## `WebWorkerClient` API

The `WebWorkerClient` class exposes an async API for communicating with a Web Worker:

```csharp
public sealed class WebWorkerClient : IAsyncDisposable
{
public static async Task<WebWorkerClient> CreateAsync(
IJSRuntime jsRuntime);

public async Task<TResult> InvokeAsync<TResult>(
string method, object[] args,
CancellationToken cancellationToken = default);

public async ValueTask DisposeAsync();
}
```

* `CreateAsync`: Initializes the worker and waits for the .NET runtime to be ready inside the worker thread.
* `InvokeAsync<TResult>`: Calls a `[JSExport]` method on the worker by its full name (`Namespace.ClassName.MethodName`) and returns the deserialized result. JSON string results are automatically parsed into `TResult`.
* `DisposeAsync`: Terminates the worker and releases resources. Use `await using` or call explicitly.

:::moniker-end

:::moniker range="< aspnetcore-11.0"

The guidance in this article mirrors the concepts from the React-focused *.NET on Web Workers* walkthrough, but adapts every step to a Blazor frontend. It highlights the same QR-code generation scenario implemented in this repository. To learn about Web Workers with React, see <xref:client-side/dotnet-on-webworkers>.

## Sample app
Expand Down Expand Up @@ -314,6 +510,8 @@ public partial class Home : ComponentBase
* Move long-running workflows into dedicated worker instances per feature area.
* Explore shared array buffers or Atomics when you need higher-throughput synchronization between Blazor and workers.

:::moniker-end

## Additional resources

<xref:client-side/dotnet-on-webworkers>
8 changes: 7 additions & 1 deletion aspnetcore/client-side/dotnet-on-webworkers.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
---
title: .NET on Web Workers
ai-usage: ai-assisted
author: guardrex
description: Learn how to use Web Workers to enable JavaScript to run on separate threads that don't block the main UI thread for improved app performance in a React app.
monikerRange: '>= aspnetcore-8.0'
ms.author: wpickett
ms.custom: mvc
ms.date: 11/20/2025
ms.date: 03/13/2026
uid: client-side/dotnet-on-webworkers
---
# .NET on Web Workers
Expand All @@ -20,6 +21,11 @@ Modern web apps often require intensive computational tasks that can block the m

This approach is particularly valuable when you need to perform complex calculations, data processing, or business logic without requiring direct DOM manipulation. Instead of rewriting algorithms in JS, you can maintain your existing .NET codebase and execute it efficiently in the background while your React.js frontend remains responsive.

> [!TIP]
> Starting with .NET 11, the `webworker` project template (`dotnet new webworker`) scaffolds the JavaScript worker scripts and interop boilerplate for you. The template works with any .NET WebAssembly host—Blazor, standalone `wasmbrowser` apps, and custom JavaScript frontends like React. Import the template's JavaScript client (`dotnet-web-worker-client.js`) directly from your app's entry point to get started. For the Blazor-specific integration that includes a C# `WebWorkerClient` class, see <xref:blazor/blazor-web-workers>.

This article demonstrates the manual approach for React.js frontends using a standalone .NET WebAssembly project.

## Sample app

Explore a complete working implementation in the [Blazor samples GitHub repository](https://github.com/dotnet/blazor-samples). The sample is available for .NET 10 or later and named `DotNetOnWebWorkersReact`.
Expand Down
2 changes: 1 addition & 1 deletion aspnetcore/web-api/jsonpatch.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ JSON Patch support in ASP.NET Core web API is based on <xref:System.Text.Json> s
To enable JSON Patch support with <xref:System.Text.Json>, install the [`Microsoft.AspNetCore.JsonPatch.SystemTextJson`](https://www.nuget.org/packages/Microsoft.AspNetCore.JsonPatch.SystemTextJson) NuGet package.

```dotnetcli
dotnet add package Microsoft.AspNetCore.JsonPatch.SystemTextJson --prerelease
dotnet add package Microsoft.AspNetCore.JsonPatch.SystemTextJson
```

This package provides a <xref:Microsoft.AspNetCore.JsonPatch.SystemTextJson.JsonPatchDocument%601> class to represent a JSON Patch document for objects of type `T` and custom logic for serializing and deserializing JSON Patch documents using <xref:System.Text.Json>. The key method of the <xref:Microsoft.AspNetCore.JsonPatch.SystemTextJson.JsonPatchDocument%601> class is <xref:Microsoft.AspNetCore.JsonPatch.SystemTextJson.JsonPatchDocument.ApplyTo(System.Object)>, which applies the patch operations to a target object of type `T`.
Expand Down
Loading