PeakSWC.RemoteBlazorWebView.RemoteComponents 11.0.0

dotnet add package PeakSWC.RemoteBlazorWebView.RemoteComponents --version 11.0.0
                    
NuGet\Install-Package PeakSWC.RemoteBlazorWebView.RemoteComponents -Version 11.0.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="PeakSWC.RemoteBlazorWebView.RemoteComponents" Version="11.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="PeakSWC.RemoteBlazorWebView.RemoteComponents" Version="11.0.0" />
                    
Directory.Packages.props
<PackageReference Include="PeakSWC.RemoteBlazorWebView.RemoteComponents" />
                    
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 PeakSWC.RemoteBlazorWebView.RemoteComponents --version 11.0.0
                    
#r "nuget: PeakSWC.RemoteBlazorWebView.RemoteComponents, 11.0.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 PeakSWC.RemoteBlazorWebView.RemoteComponents@11.0.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=PeakSWC.RemoteBlazorWebView.RemoteComponents&version=11.0.0
                    
Install as a Cake Addin
#tool nuget:?package=PeakSWC.RemoteBlazorWebView.RemoteComponents&version=11.0.0
                    
Install as a Cake Tool

Remote components

Run a Blazor component in its own process and show it inside your app, in a sandboxed iframe. Use it when a piece of UI should be isolated from the app: it can run with fewer rights than the app, crash without taking the app down, and can't touch the app's page.

 app process                                        component process (RemoteComponentHost)
 ┌──────────────────────────────┐   stdin: "set"    ┌──────────────────────────────┐
 │ <MyFrame Open=... Value=...> │ ────────────────▶ │ MyReceiver (contract values) │
 │   iframe sandbox, opaque     │ ◀──────────────── │ root component (your UI)     │
 │   origin, height only        │  stdout: "call"   │ RemoteBlazorWebView client   │
 └──────────────┬───────────────┘                   └──────────────┬───────────────┘
                └──── iframe src = <relay>/<id> ◀── relay ◀────────┘
  • Data goes over the process's stdin/stdout, newline-delimited JSON (RemoteComponentMessage): the app sends set with the contract values that changed, the component sends call for a callback. Nothing crosses through the browser, so when the app is viewed remotely, the remote browser can't forge either side.
  • The iframe is sandboxed without allow-same-origin (RemoteComponentFrame.DefaultSandbox), so it has an opaque origin. The only postMessage the app's page accepts from it is its height (rbwv-remote-height), matched by the sending window.

Mirrors

When the app's page is mirrored (a read-only /mirror/<id> view), the mirror renders the same page, frame included. If the mirror loaded the component's page as well, the relay would take that as a reload and the component's process would restart. So the frame is rendered without src: data-rbwv-src holds the component's page, and data-rbwv-mirror-src the relay's read-only view of it (RemoteComponentSession.MirrorSource, by default <relay>/mirror/<id>). The app's page loads the first (remotecomponents.js); a mirror page loads the second (the RemoteWebView script), or nothing when the session has no mirror source. The component's process allows mirrors by default (RemoteComponentHostOptions.EnableMirrors). Mirrors can't click or type in it, like the rest of the mirror.

A contract, and what the generator writes from it

Declare what crosses between the two processes as an interface, in a library both sides reference:

[RemoteComponentContract]
public interface IClock
{
    string TimeZone { get; }              // a value: app -> component
    Alarm? NextAlarm { get; }             // any JSON-serializable type
    Task Snooze();                         // a callback: component -> app
    Task Picked(DateTimeOffset when);      // at most one argument
}

The source generator in this package writes:

  • ClockFrame for the app, a component with [Parameter] TimeZone, NextAlarm, EventCallback Snooze and EventCallback<DateTimeOffset> Picked:

    <ClockFrame Open="ct => launcher.StartAsync(ct)" TimeZone="@zone" NextAlarm="@alarm" Snooze="OnSnooze" Title="Clock" />
    
  • ClockReceiver for the component's process: implements IClock with the values the app sent (Changed fires when they change); calling Snooze() sends the callback. Register it in the host and inject IClock (or ClockReceiver) in the UI:

    return RemoteComponentHost.Run(new RemoteComponentHostOptions
    {
        ServerUri = relay, GrpcBaseUri = grpc, Id = id, ExitWhenInputCloses = true, RootComponent = typeof(ClockRoot),
        ConfigureServices = (services, connection) => services.AddRemoteComponentReceiver<ClockReceiver>(connection),
    });
    

Values and arguments must be JSON-serializable (RemoteComponentJson.Options: web defaults, enums as strings). Values are sent when they change (compared as JSON), all of them the first time. Rules the generator enforces: RBWRC001 for a member that isn't a get-only property or a Task method with at most one argument, RBWRC002 for a generic or nested interface, RBWRC003 for a member name already defined on RemoteComponentFrameBase or RemoteComponentReceiverBase. Names can be set with [RemoteComponentContract(FrameName = "...", ReceiverName = "...")].

Razor compiles before source generators run, so a .razor file can't use a frame generated in its own project: keep the contract in a separate library.

The pieces

Side Type Package
App RemoteComponentFrameBase: the iframe, sends values, raises callbacks. Open starts the process and returns a RemoteComponentSession (the frame's address and the RemoteComponentConnection to the process) this one
Component process RemoteComponentHost.Run(options): the never-shown WPF window with a RemoteBlazorWebView client of the relay; exits when the frame closes or reloads, or the app exits PeakSWC.RemoteBlazorWebView.Wpf
Component process RemoteComponentReceiverBase: holds the values the app sent, raises Changed, sends callbacks; services.AddRemoteComponentReceiver<T>(connection) this one

How the component's process notices that the app has gone: with ExitWhenInputCloses = true it exits when its stdin ends, which Windows does as soon as the app exits, crashes or is killed (the app must keep its end of the pipe open while the component is shown, as RemoteComponentSession does). Without a pipe on stdin, ParentProcessId polls the process list every 2 seconds instead.

How the app starts the process (token, folder, relay) is up to it: Open is a function you pass to the frame. The relay is usually an embedded one (PeakSWC.RemoteWebViewService.Lib) with FrameAncestors set to the app's origin and AllowOpaqueOrigin = true.

The component's host page

Include the height script (RemoteComponentFrame.HeightScript) so the app can size the frame:

<script>
(function () {
    var last = 0;
    function post() {
        var h = Math.ceil(document.documentElement.scrollHeight);
        if (h !== last) { last = h; window.parent.postMessage({ type: 'rbwv-remote-height', height: h }, '*'); }
    }
    if (window.ResizeObserver) new ResizeObserver(post).observe(document.documentElement);
    window.addEventListener('load', post);
})();
</script>
<script src="_framework/blazor.webview.js"></script>
Product Compatible and additional computed target framework versions.
.NET net11.0 is compatible. 
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
11.0.0 0 11/11/2026
10.0.15.1 0 11/11/2026
10.0.15 39 11/11/2026
10.0.14 103 11/8/2026

Based on maui 11.0.0

Changes since 11.0.0-rc2
Merge branch 'claude/net11-port-shared-control': net10's shared control work on the released .NET 11 (11.0.0)
net11: no pack hook or package reference that net11 had already removed
Release process doc: net11 numbers and how EditWebView runs on this line
Upstream source from aspnetcore 11.0.0 and MAUI 11.0.10 (EditWebView run)
net11: keep net11's own module-initializer end-to-end tests
net11: the ported cache test passes the cache-control value net11's helper needs
net11 on the released .NET 11: SDK 11.0.100, packages 11.0.0, version 11.0.0
Port net10 since the last sync: shared control, owner key, sign-in providers, opt-in session bar
Document EditWebView in the release process.
Merge pull request #80 from budcribar/claude/net11-escape-release-notes
Escape commas and semicolons in the release notes passed to dotnet pack
Merge pull request #77 from budcribar/claude/net11-port-net10-release
Retarget the ported net10 projects to net11
Prepare NuGet release packaging, CI test coverage, and remote component contract hardening
RemoteComponentHost: ExitWhenInputCloses, exit when stdin ends instead of polling
Keep remote component frames from restarting when the app is mirrored
Mark agent registry (AgentHub/AgentAdmin) experimental (RWV001)
Restore Authorization package references in RemoteWebViewService.Lib
Describe agents and the embedded relay without naming one app
Remote components README: use a generic contract example
Remote components: describe the use generically
Add the [RemoteComponentContract] source generator
Add remote components: runtime and the WPF RemoteComponentHost
Add ReverseMirror to [RemoteBlazor]
Add the [RemoteBlazor] source generator
Port the net9 test-runner and window-sizing fixes.
Install nvm-windows when RunTests.ps1 cannot find nvm.
Throw the frame-ancestor error after the scan.
Name the first invalid frame ancestor after valid ones.
Merge pull request #66 from budcribar/net11-bugbounty-fixes
Port Bug02, Bug03 (dispose-on-throw), Bug06, Bug15, Bug44, and Bug60 fixes and regression tests
Pin net11 to the rc.3 SDK and the patched Graph/Kiota line.
Update net11 dependencies to current rc.3 and stable releases.
Pin global.json to the SDK shipped by the 11.0-preview image.
Fix Sonar reliability and security findings that are safe to backport.
Merge pull request #65 from budcribar/claude/net11-port-net10
Target net11 in the ported embedded-relay library, sample and server branch CI
Let pages in opaque-origin sandboxed iframes connect
Synchronize primary browser scrolling to read-only mirrors
EmbeddedRelayWpf: embed wwwroot so a single-file publish works
EmbeddedRelayWpf: target Windows 10 SDK and show crashes
Copy RemoteWebViewService.Lib into the Docker build and server CI paths
Split RemoteWebViewService into an embeddable library for net10
Gate AgentAdmin starts with AGENT_ADMIN_KEY and guard in-flight starts
Add the agent registry (AgentHub / AgentAdmin) to RemoteWebViewService
Add Linux container mode with Kestrel TLS/HTTP2 for Railway TCP proxy.
Remove net10-only Program.IsContainerMode reference from Startup.cs on net11
Add in-memory event log provider for GetLoggedEvents on Linux
Fail the performance check when the baseline has no samples.
Add the net11 intermittent CI failure log.
Record the nuget.org user and required reviewer.
Generate PackageReleaseNotes for the maintainer to commit.
Spell package suffixes without a dot and add a patch counter.
Draft the package release workflow without a NuGet push.
Add a draft release workflow skeleton.
Read Chrome output on a dedicated thread before waiting for DevTools.
Retry a timed-out DevTools probe and keep the Chrome module classes in one collection.
Measure TestConnectedEvent from the Connected signal, not the pause.
Note SYSLIB1044 on the base-href regex as won't fix.

Full list: https://github.com/budcribar/RemoteBlazorWebView/compare/11.0.0-rc2...24c67bf600e845754a3edfc138ccc97c6f2ccb00