SpawnDev.BlazorJS detects the JavaScript global scope at startup and exposes it through BlazorJSRuntime. This is critical for applications that run in both browser Window and Web Worker contexts - the same Program.cs handles both, but different code paths execute depending on the scope.
Namespace: SpawnDev.BlazorJS
public enum GlobalScope
{
None = 0,
Default = 0,
Window = 1,
DedicatedWorker = 2,
SharedWorker = 4,
ServiceWorker = 8,
}The enum supports flags, allowing you to test for multiple scopes at once.
| Property | Returns true when... |
|---|---|
JS.IsWindow |
Running in a browser Window (DOM available) |
JS.IsWorker |
Running in any Worker scope |
JS.IsDedicatedWorkerGlobalScope |
Running in a Dedicated Worker |
JS.IsSharedWorkerGlobalScope |
Running in a Shared Worker |
JS.IsServiceWorkerGlobalScope |
Running in a Service Worker |
if (JS.IsWindow)
{
// DOM is available - can add components, access document, etc.
}
if (JS.IsWorker)
{
// No DOM - service-only mode
// This is true for ALL worker types
}
if (JS.IsDedicatedWorkerGlobalScope)
{
// Dedicated Worker - has a single parent
}
if (JS.IsSharedWorkerGlobalScope)
{
// Shared Worker - can be accessed from multiple pages
}
if (JS.IsServiceWorkerGlobalScope)
{
// Service Worker - intercepts network requests
}Test for specific scope flags:
// True if running in a Window OR a DedicatedWorker
if (JS.IsScope(GlobalScope.Window | GlobalScope.DedicatedWorker))
{
// ...
}Access the raw enum value:
GlobalScope scope = JS.GlobalScope;
switch (scope)
{
case GlobalScope.Window:
Console.WriteLine("Browser window");
break;
case GlobalScope.DedicatedWorker:
Console.WriteLine("Dedicated worker");
break;
case GlobalScope.SharedWorker:
Console.WriteLine("Shared worker");
break;
case GlobalScope.ServiceWorker:
Console.WriteLine("Service worker");
break;
}BlazorJSRuntime provides typed references to the global object based on scope:
| Property | Type | Non-null when... |
|---|---|---|
JS.GlobalThis |
JSObject? |
Always (the globalThis object) |
JS.WindowThis |
Window? |
IsWindow == true |
JS.DedicateWorkerThis |
DedicatedWorkerGlobalScope? |
IsDedicatedWorkerGlobalScope == true |
JS.SharedWorkerThis |
SharedWorkerGlobalScope? |
IsSharedWorkerGlobalScope == true |
JS.ServiceWorkerThis |
ServiceWorkerGlobalScope? |
IsServiceWorkerGlobalScope == true |
The standard pattern for scope-aware Program.cs:
using SpawnDev.BlazorJS;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
var builder = WebAssemblyHostBuilder.CreateDefault(args);
// Get BlazorJSRuntime immediately for scope detection
builder.Services.AddBlazorJSRuntime(out var JS);
// Only add root components in Window scope
// Workers don't have a DOM - adding components would fail
if (JS.IsWindow)
{
builder.RootComponents.Add<App>("#app");
builder.RootComponents.Add<HeadOutlet>("head::after");
}
// Register services (available in ALL scopes)
builder.Services.AddSingleton<MyService>();
// BlazorJSRunAsync handles the difference:
// - In Window: calls RunAsync() to start Blazor renderer
// - In Worker: enters service-only mode (awaits forever)
await builder.Build().BlazorJSRunAsync();using SpawnDev.BlazorJS;
using SpawnDev.BlazorJS.WebWorkers;
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.Services.AddBlazorJSRuntime(out var JS);
if (JS.IsWindow)
{
builder.RootComponents.Add<App>("#app");
builder.RootComponents.Add<HeadOutlet>("head::after");
}
// WebWorkerService lets you spawn workers from .NET
builder.Services.AddWebWorkerService();
// Services registered here run in BOTH Window and Worker scopes
builder.Services.AddSingleton<ComputeService>();
await builder.Build().BlazorJSRunAsync();builder.Services.AddBlazorJSRuntime(out var JS);
if (JS.IsWindow)
{
builder.RootComponents.Add<App>("#app");
builder.RootComponents.Add<HeadOutlet>("head::after");
// Window-only services
builder.Services.AddSingleton<UIService>();
builder.Services.AddSingleton<NavigationService>();
}
if (JS.IsWorker)
{
// Worker-only services
builder.Services.AddSingleton<BackgroundProcessingService>();
}
// Services for all scopes
builder.Services.AddSingleton<DataService>();
builder.Services.AddSingleton<CryptoService>();BlazorJSRunAsync() is a scope-aware replacement for RunAsync(). It:
- Starts all background services (IBackgroundService, IAsyncBackgroundService, and services registered with compatible GlobalScope values)
- If running in a Window scope: calls
RunAsync()to start the Blazor renderer - If running in a Worker scope: enters an infinite await (service-only mode) - no renderer, no components, just services
This means the same compiled WASM binary runs in both contexts, with the scope determining which code paths execute.
Some features (like SharedArrayBuffer) require cross-origin isolation:
if (JS.CrossOriginIsolated == true)
{
// SharedArrayBuffer is available
// Required for multi-threaded workers with shared memory
}Cross-origin isolation requires the server to send:
Cross-Origin-Opener-Policy: same-originCross-Origin-Embedder-Policy: require-corp
- Getting Started - Program.cs setup
- BlazorJSRuntime - Full runtime reference